Qual é o significado da propriedade do construtor de JavaScript?

Tentando dobrar de cabeça em torno de JavaScript assumir OO … e, como muitos outros, correndo em confusão sobre a propriedade do constructor . Em particular, o significado da propriedade do constructor , como não consigo fazer com que tenha algum efeito. Por exemplo:

 function Foo(age) { this.age = age; } function Bar() { this.name = "baz"; } Bar.prototype = new Foo(42); var b = new Bar; alert(b.constructor); // "Foo". That's OK because we inherit `Foo`'s prototype. alert(b.name); // "baz". Shows that Bar() was called as constructor. alert(b.age); // "42", inherited from `Foo`. 

No exemplo acima, o object b parece ter o construtor correto chamado ( Bar ) – e herda a propriedade age de Foo . Então, por que muitas pessoas sugerem isso como um passo necessário:

 Bar.prototype.constructor = Bar; 

Claramente, o construtor de Bar correto foi chamado ao construir b , então qual o impacto que essa propriedade de protótipo tem? Estou curioso para saber que diferença prática isso realmente faz para que a propriedade de construtor seja definida ‘corretamente’ – já que não consigo ver se ela tem algum efeito sobre qual construtor é realmente chamado depois que um object é criado.

    A propriedade do constructor não faz absolutamente nenhuma diferença prática para nada internamente. Só é útil se o seu código o usar explicitamente. Por exemplo, você pode decidir que precisa que cada um de seus objects tenha uma referência à function construtora real que o criou; Nesse caso, você precisará definir a propriedade do constructor explicitamente ao configurar a inheritance atribuindo um object à propriedade de prototype uma function de construtor, como em seu exemplo.

    O primeiro passo é entender o constructor e o prototype . Não é difícil, mas é preciso abandonar a “inheritance” no sentido clássico.

    O construtor

    A propriedade constructor não causa efeitos particulares em seu programa, exceto que você pode ver qual function foi usada em conjunto com o operador new para criar seu object. Se você digitou o new Bar() será Bar e você digitou o new Foo será Foo .

    O protótipo

    A propriedade prototype é usada para pesquisa, caso o object em questão não tenha a propriedade solicitada. Se você escrever x.attr , o JavaScript tentará encontrar attr entre os atributos de x . Se não conseguir encontrá-lo, ele será x.__proto__ em x.__proto__ . Se não estiver lá, ele será x.__proto__.__proto__ em x.__proto__.__proto__ e assim por diante, desde que __proto__ seja definido.

    Então, o que é __proto__ e o que isso tem a ver com o prototype ? Em __proto__ , o prototype é para “tipos”, enquanto o __proto__ é para “instâncias”. (Eu digo isso com aspas porque não há realmente nenhuma diferença entre tipos e instâncias). Quando você escreve x = new MyType() , o que acontece (entre outras coisas) é que x.__proto___ está definido como MyType.prototype .

    A questão

    Agora, o acima deve ser tudo o que você precisa para derivar o que seu próprio exemplo significa, mas tentar responder sua pergunta real; “por que escrever algo como”:

    Bar.prototype.constructor = Bar;

    Eu pessoalmente nunca vi isso e acho um pouco bobo, mas no contexto que você deu, isso significará que o object Bar.prototype (criado usando o new Foo(42) ) Bar.prototype como sendo criado pelo Bar em vez de Foo . Eu suponho que a ideia é que alguns façam algo semelhante a linguagens C ++ / Java / C # -like em que uma pesquisa de tipo (a propriedade de constructor ) sempre produzirá o tipo mais específico em vez do tipo do object mais genérico no protótipo. cadeia.

    Meu conselho: não pense muito sobre “inheritance” em JavaScript. Os conceitos de interfaces e mixins fazem mais sentido. E não verifique objects para seus tipos. Verifique as propriedades requeridas (“se andar como um pato e for como um pato, é um pato”).

    Tentando forçar o JavaScript em um modelo de inheritance clássica, quando tudo o que ele tem é o mecanismo de protótipo, como descrito acima, é o que causa a confusão. As muitas pessoas que sugeriram definir manualmente a propriedade de constructor provavelmente tentaram fazer exatamente isso. As abstrações são boas, mas essa atribuição manual da propriedade do construtor não é um uso muito idiomático do JavaScript.

    um caso para usar o construtor:

    1. esta é uma das realizações comuns da inheritance:

       Function.prototype.extend = function(superClass,override) { var f = new Function(); f.prototype = superClass.prototype; var p = this.prototype = new f(); p.constructor = this; this.superclass = superClass.prototype; ... }; 
    2. este new f() não chamaria o construtor de superClass, então quando você cria uma subclass, talvez você precise chamar o superClass primeiro, assim:

       SubClass = function() { SubClass.superClass.constructor.call(this); }; 

    então a propriedade do construtor faz sentido aqui.

    Um dos casos de uso em que você gostaria que a propriedade prototype.constructor sobrevivesse à prototype propriedade prototype é quando você define um método no prototype que produz novas instâncias do mesmo tipo que a instância fornecida. Exemplo:

     function Car() { } Car.prototype.orderOneLikeThis = function() { // Clone producing function return new this.constructor(); } Car.prototype.advertise = function () { console.log("I am a generic car."); } function BMW() { } BMW.prototype = Object.create(Car.prototype); BMW.prototype.constructor = BMW; // Resetting the constructor property BMW.prototype.advertise = function () { console.log("I am BMW with lots of uber features."); } var x5 = new BMW(); var myNewToy = x5.orderOneLikeThis(); myNewToy.advertise(); // => "I am BMW ..." if `BMW.prototype.constructor = BMW;` is not // commented; "I am a generic car." otherwise. 

    A propriedade constructor aponta para o construtor que foi usado para criar a instância do object. Se você digitou ‘new Bar ()’ será ‘Bar’ e você digitou ‘new Foo ()’ será ‘Foo’.

    Mas se você definir o protótipo sem definir o construtor, você obteria algo assim:

     function Foo(age) { this.age = age; } function Bar() { this.name = "baz"; } Bar.prototype = new Foo(42); var one = new Bar(); console.log(one.constructor); // 'Foo' var two = new Foo(); console.log(two.constructor); // 'Foo' 

    Para definir o construtor, na verdade, para o construtor que foi usado para criar o object, precisamos definir o construtor também enquanto definimos o protótipo da seguinte forma:

     function Foo(age) { this.age = age; } function Bar() { this.name = "baz"; } Bar.prototype = new Foo(42); Bar.prototype.constructor = Bar; var one = new Bar(); console.log(one.constructor); // 'Bar' var two = new Foo(); console.log(two.constructor); // 'Foo'