javascript - Varför tilldela Something till Something.prototype.constructor?


Jag läste om hur Javascript-prototype-egenskapen fungerar tillsammans med arv och började sedan titta igenom Angular.js-koden och kom på några frågor.

För det första läste jag att prototype-egenskapen pekar på ett objekt som har en "constructor"-egenskap som pekar tillbaka till den ursprungliga funktionen som används för att skapa objektet. Så till exempel:

// This is the constructor
function Shape() {
    this.position = 1;
}

// The constructor points back to the original function we defined
Shape.protoype.constructor == Shape;

Prototypen innehåller också alla andra metoder eller egenskaper som har definierats på den av oss eller av Javascript-språket själv, och dessa delas av alla instanser av objektet. Om du vill att ett objekt som heter Square ska ärva från Shape måste du sätta Squares prototyp lika med en ny instans av Shape eftersom den interna [[prototype]]-egenskapen hos Square.prototype sätts till det publika objektsvärdet av Shape.prototype-egenskapen.

function Square() {}
Square.prototype = new Shape();
var square = new Square();
square.position; // This will output 1

Allt detta är vettigt för mig.

Men Angular.js-koden som jag har en fråga om verkar vara relaterad till allt detta men gör något som jag inte förstår. Det verkar inte hantera arv, så jag kan förstå varför det skulle finnas skillnader, men jag är bara nyfiken på varför de skrev det på det sättet de gjorde.

Inom Angular.js finns det ett HashMap-objekt och ett Lexer-objekt, men de är definierade på olika sätt men verkar ändå instansieras och användas på exakt samma sätt. Lexer-konstruktören definieras först, och sedan sätter de prototypen till ett objektliteral som innehåller metoder som ska delas av alla instanser av Lexer. Allt detta är logiskt. Det jag inte förstår är varför de specificerar egenskapen "constructor" och sätter den till bara "Lexer" när de inte gör det för HashMap nedan.

var Lexer = function(options) {
    this.options = options;
};

// Notice they specify Lexer as the constructor even though they don't for HashMap below
Lexer.prototype = {
    constructor: Lexer,
    lex: function(text) { ... },
    is: function(ch, chars) { ... },
    peek: function(i) { ... },
    isNumber: function(ch) { ... },
    isWhitespace: function(ch) { ... },
    isIdent: function(ch) { ... },
    isExpOperator: function(ch) { ... },
    throwError: function(error, start, end) { ... },
    readNumber: function() { ... },
    readIdent: function() { ... },
    readString: function(quote) { ... }
};

Om du sedan tittar på HashMap-koden gör de samma sak förutom att de inte anger konstruktörsegenskapen. Varför är det så? Det verkar fungera på exakt samma sätt, och jag har testat att konstruktören fortfarande anropas.

// The HashMap Constructor
function HashMap(array, isolatedUid) {
    if (isolatedUid) {
        var uid = 0;
        this.nextUid = function() {
            return ++uid;
        };
    }
    forEach(array, this.put, this);
}

HashMap.prototype = {
    put: function(key, value) { ... },
    get: function(key) { ... },
    remove: function(key) { ... }
};

Så är konstruktor-egenskapen valfri när det inte finns något arv, så kanske en person skrev Lexern och en annan HashMap och en bestämde sig för att specificera konstruktorn?

Svar #1

Som standard har varje prototyp en constructor-egenskap som refererar till den funktion som den "hör" till.

function A() {
}

console.log(A.prototype.constructor === A); // true

Om du skriver över ett prototyp helt med ett objektliteral eller med någon annan konstruerad prototyp, kommer detta constructor-värde att raderas och bli odefinierat eller få något annat värde.

function A() {
}
A.prototype = {
    greeting: "Hello!"
};

console.log(A.prototype.constructor === A); // false

Prototypens constructor-egenskap behövs inte för att konstruktorn ska fungera korrekt, men människor kommer ofta att tilldela prototype.constructor tillbaka till sitt ursprungliga värde för att upprätthålla konsekvens, och det är vad du ser för Lexer.

Överväg :

function A() {
}

function B() {
}

B.prototype = Object.create(A.prototype);

var b = new B();

console.log(b.constructor === A); // true
console.log(b.constructor === B); // false

B.prototype.constructor = B;

console.log(b.constructor === A); // false
console.log(b.constructor === B); // true

Man kan bara spekulera i varför detta utelämnades för HashMap. Jag tvivlar på att det gjordes av någon god anledning och det kan ha varit ett förbiseende.