2010-10-25 19 views
114

Intentando doblar por la cabeza alrededor de Javascript toma OO ... y, como muchos otros, se encuentra con la confusión sobre la propiedad constructor. En particular, la importancia de la propiedad constructor, ya que no puedo hacer que tenga ningún efecto. Ej .:¿Cuál es la importancia de la propiedad del constructor de Javascript?

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`. 

En el ejemplo anterior, el objeto b parece haber tenido el constructor derecha llamado (Bar) – y se hereda la propiedad de edad Foo. Entonces, ¿por qué muchas personas sugieren esto como un paso necesario:

Bar.prototype.constructor = Bar; 

Claramente, el constructor derecho Bar fue llamado al construir b, así que ¿Qué impacto tiene esta propiedad prototipo? Tengo curiosidad por saber qué diferencia práctica realmente tiene hacer que la propiedad del constructor se establezca "correctamente", ya que no veo que tenga ningún efecto sobre qué constructor se llama realmente después de que se crea un objeto.

+0

he explicado la relación entre prototipo y constructor en: http://stackoverflow.com/a/23877420/895245 –

Respuesta

66

La propiedad constructor no hace absolutamente ninguna diferencia práctica a nada internamente. Es solo de utilidad si su código lo usa explícitamente. Por ejemplo, puede decidir que necesita que cada uno de sus objetos tenga una referencia a la función constructora real que lo creó; de ser así, deberá establecer la propiedad constructor explícitamente cuando configure la herencia asignando un objeto a la propiedad prototype de la función del constructor, como en su ejemplo.

+0

El 'constructor' es una parte importante de ser capaz de calce objeto' .getPrototypeOf'. Yo personalmente uso 'Object.getPrototypeOf' y supongo que no todos rompen la relación' constructor' 'prototype' <-> – Raynos

+0

@Raynos: No es la suposición más segura, pero probablemente más segura de lo que solía ser. Retrocede unos años y casi todos los tutoriales de herencia de objetos de JavaScript en la web fueron influenciados por Java y se recomienda configurar manualmente la propiedad 'constructor'. Tú y yo tenemos una ética bastante diferente: prefiero los envoltorios a las calzas, y he vivido sin 'Object.getPrototypeOf' durante tanto tiempo que no tengo un deseo ardiente por eso ahora. –

+1

El punto principal es que deberíamos advertir a las personas que para el cumplimiento de ES5 esta relación no debería romperse en navegadores más antiguos – Raynos

97

El primer paso es comprender de qué se tratan constructor y prototype. No es difícil, pero hay que abandonar la "herencia" en el sentido clásico.

El constructor

El constructor propiedad no se causa se utilizó ningún efecto particular en su programa, excepto la que se puede ver en él para ver que funcionan en conjunto con el operador new a crear el objeto de . Si escribió new Bar() será Bar y escribió new Foo será Foo.

El prototipo

La propiedad prototype se utiliza para las operaciones de búsqueda en caso de que el objeto en cuestión no tiene la propiedad pidió. Si escribe x.attr, JavaScript intentará encontrar attr entre los atributos de x. Si no puede encontrarlo, se verá en x.__proto__. Si tampoco está allí, se verá en x.__proto__.__proto__, y así sucesivamente, siempre que se defina __proto__.

¿Qué es __proto__ y qué tiene que ver con prototype? En pocas palabras, prototype es para "tipos", mientras que __proto__ es para "instancias". (Lo digo con comillas porque en realidad no hay ninguna diferencia entre tipos e instancias). Cuando escribe x = new MyType(), lo que sucede (entre otras cosas) es que x.__proto___ se establece en MyType.prototype.

La pregunta

Ahora bien, lo anterior debe ser todo lo que necesita para derivar el significado de su propio ejemplo, sino para tratar de responder a su pregunta real; "¿Por qué escribir algo como":

Bar.prototype.constructor = Bar;

Yo personalmente nunca he visto y me parece un poco tonto, pero en el contexto que ha dado que significará que el -objeto Bar.prototype (creado mediante el uso de new Foo(42)) se posarán como creados por Bar en lugar de Foo. Supongo que la idea es hacer algo similar a los lenguajes C++/Java/C# donde una búsqueda de tipo (la propiedad constructor) siempre producirá el tipo más específico en lugar del tipo del objeto más genérico que está más arriba en el prototipo- cadena.

Mi consejo: no creo mucho acerca de "herencia" en JavaScript. Los conceptos de interfaces y mixins tienen más sentido. Y no verifique los objetos para sus tipos. Verifique las propiedades requeridas en su lugar ("si camina como un pato y grazna como un pato, es un pato").

Intentando forzar a JavaScript en un modelo de herencia clásico, cuando todo lo que tiene es el prototipo de mecanismo como se describió anteriormente, es lo que causa la confusión. Las muchas personas que sugirieron configurar manualmente la-propiedad probablemente trataron de hacer precisamente eso. Las abstracciones son buenas, pero esta asignación manual de la propiedad del constructor no es un uso muy idiomático de JavaScript.

+1

Estoy de acuerdo en que la propiedad 'constructor' no es muy útil, pero heredar del prototipo de otro objeto puede ser extremadamente poderoso y no desalentaría eso en absoluto. –

+0

Gracias - puede que no sea idiomático, pero muchos de los tutoriales en la red (y q/a en SE) sugieren que utilizar este método particular es 'necesario', como si todo el prototipo se rompe si no lo usa. – aaa90210

+1

Oh, lo siento si así fue como sucedió. Heredar de otro prototipo de objetos no es el problema; alterando la propiedad del constructor es. – Jakob

10

un caso de usar constructor:

  1. esta es una de la realización común de la herencia:

    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() no sería llamar al constructor de la superclase de modo que cuando se crea una subclase, tal vez necesite llamar a la superclase al principio, así:

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

por lo que la propiedad del constructor tiene sentido aquí.

+1

Sí, pero puede escribir 'SubClass.superClass.call (this);'. –

+0

A menos que planee hacer referencia a la propiedad del constructor en la instancia, puede comentar con seguridad la línea 'p.constructor = this' y nada cambia. La respuesta puede mostrar el caso * cuando * se usa la propiedad 'constructor', pero no explica * por qué * se usa allí. En resumen, esta respuesta no responde a la pregunta original sobre el significado de la propiedad 'constructor'. – golem

+0

1. ¿Puedes codificar un ejemplo de uso simple? 2. ¿Cuál es el rol de 'anular'? – HeyJude

0

La propiedad del constructor apunta al constructor que se utilizó para crear la instancia del objeto. Si escribió 'new Bar()' será 'Bar' y habrá escrito 'new Foo()' será 'Foo'.

Pero si se establece el prototipo sin ajustar el constructor, se llega a algo como esto:

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 establecer el constructor en realidad para el constructor que se utilizó para crear el objeto, es necesario establecer el constructor, así mientras que la creación de prototipos de la siguiente manera:

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' 
2

Uno de los casos de uso cuando sería deseable que la propiedad prototype.constructor para sobrevivir prototype reasignación propiedad es cuando se define un método en el prototype que produce nuevas instancias del mismo tipo que la instancia dada.Ejemplo:

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. 
+0

¿Por qué no prefirió 'Object.setPrototypeOf (BMW.prototype, Car.prototype);' en lugar de 'BMW.prototype = Object.create (Car.prototype);'? – overexchange

Cuestiones relacionadas