¿Por qué es necesario configurar el constructor del prototipo?
En la sección sobre herencia en el artículo de MDN Introducción a Javascript orientado a objetos , noté que configuraron el prototipo.constructor:
// correct the constructor pointer because it points to Person
Student.prototype.constructor = Student;
¿Tiene esto algún propósito importante? ¿Está bien omitirlo?
No siempre es necesario, pero tiene sus usos. Supongamos que queremos crear un método de copia en la Person
clase base. Como esto:
// define the Person Class
function Person(name) {
this.name = name;
}
Person.prototype.copy = function() {
// return new Person(this.name); // just as bad
return new this.constructor(this.name);
};
// define the Student class
function Student(name) {
Person.call(this, name);
}
// inherit Person
Student.prototype = Object.create(Person.prototype);
Ahora bien, ¿qué pasa cuando creamos uno nuevo Student
y lo copiamos?
var student1 = new Student("trinth");
console.log(student1.copy() instanceof Student); // => false
La copia no es una instancia de Student
. Esto se debe a que (sin comprobaciones explícitas), no tendríamos forma de devolver una Student
copia de la clase "base". Sólo podemos devolver un Person
. Sin embargo, si hubiéramos reiniciado el constructor:
// correct the constructor pointer because it points to Person
Student.prototype.constructor = Student;
...entonces todo funciona como se esperaba:
var student1 = new Student("trinth");
console.log(student1.copy() instanceof Student); // => true
¿Tiene esto algún propósito importante?
Si y no.
En ES5 y versiones anteriores, JavaScript en sí no se usaba constructor
para nada. Definía que el objeto predeterminado en la prototype
propiedad de una función lo tendría y que se referiría a la función, y eso era todo . Nada más en la especificación se refería a ello.
Eso cambió en ES2015 (ES6), que comenzó a usarlo en relación con las jerarquías de herencia. Por ejemplo, Promise#then
utiliza la constructor
propiedad de la promesa a la que la invoca (a través de SpeciesConstructor ) al crear la nueva promesa para regresar. También participa en la subtipificación de matrices (a través de ArraySpeciesCreate ).
Fuera del lenguaje en sí, a veces la gente lo usaba cuando intentaba construir funciones genéricas de "clonación" o simplemente cuando querían referirse a lo que creían que sería la función constructora del objeto. Mi experiencia es que usarlo es raro, pero a veces la gente lo usa.
¿Está bien omitirlo?
Está ahí de forma predeterminada, solo necesitas volver a colocarlo cuando reemplazas elprototype
objeto en la propiedad de una función :
Student.prototype = Object.create(Person.prototype);
Si no haces esto:
Student.prototype.constructor = Student;
... luego Student.prototype.constructor
hereda del Person.prototype
que (presumiblemente) tiene constructor = Person
. Entonces es engañoso. Y, por supuesto, si está subclasificando algo que lo usa (como Promise
o Array
) y no usa class
¹ (que maneja esto por usted), querrá asegurarse de configurarlo correctamente. Básicamente: es una buena idea.
Está bien si nada en su código (o el código de la biblioteca que usa) lo usa. Siempre me he asegurado de que estuviera correctamente conectado.
Por supuesto, con la class
palabra clave de ES2015 (también conocida como ES6), la mayoría de las veces la habríamos usado, ya no tenemos que hacerlo, porque la manejamos nosotros cuando lo hacemos.
class Student extends Person {
}
¹ "... si estás subclasificando algo que lo usa (como Promise
o Array
) y no usa class
..." - Es posible hacer eso, pero es una verdadera molestia (y un poco tonto). Tienes que usar Reflect.construct
.