En la sección sobre herencia en el artículo de MDN Introducción al Javascript orientado a objetos , noté que configuraron el prototype.constructor:
// correct the constructor pointer because it points to Person
Student.prototype.constructor = Student;
¿Sirve esto para algún propósito importante? ¿Está bien omitirlo?
javascript
oop
inheritance
noveno
fuente
fuente
subclass.prototype.constructor
apuntaráparent_class
si no escribessubclass.prototype.constructor = subclass
; Es decir, usarsubclass.prototype.constructor()
directamente producirá un resultado inesperado.Respuestas:
No siempre es necesario, pero tiene sus usos. Supongamos que queremos hacer un método de copia en la
Person
clase base . Me gusta esto:¿Qué sucede cuando creamos uno nuevo
Student
y lo copiamos?La copia no es una instancia de
Student
. Esto se debe a que (sin verificaciones explícitas), no tendríamos forma de devolver unaStudent
copia de la clase "base". Solo podemos devolver aPerson
. Sin embargo, si hubiéramos reiniciado el constructor:... entonces todo funciona como se esperaba:
fuente
constructor
atributo no tiene ningún significado especial en JS, por lo que también podría llamarlobananashake
. La única diferencia es que el motor se inicializa automáticamenteconstructor
enf.prototype
cada vez que se declara una funciónf
. Sin embargo, se puede sobrescribir en cualquier momento.constructor
significa que lo hace tener un significado especial en JS, casi por definición.return new this.constructor(this.name);
lugar de hacerloreturn new Person(this.name);
. Comothis.constructor
es laStudent
función (porque la configuró conStudent.prototype.constructor = Student;
), lacopy
función termina llamando a laStudent
función. No estoy seguro de cuál fue su intención con el//just as bad
comentario.Student
constructor hubiera agregado un argumento adicional comoStudent(name, id)
:? ¿Entonces tenemos que anular lacopy
función, llamar a laPerson
versión desde dentro y luego copiar laid
propiedad adicional ?Si y no.
En ES5 y versiones anteriores, el JavaScript en sí no se usaba
constructor
para nada. Definió que el objeto predeterminado en laprototype
propiedad de una función lo tendría y que se referiría nuevamente a la función, y eso fue todo . Nada más en la especificación se refería a ella en absoluto.Eso cambió en ES2015 (ES6), que comenzó a usarlo en relación con las jerarquías de herencia. Por ejemplo,
Promise#then
usa laconstructor
propiedad de la promesa a la que la invocas (a través de SpeciesConstructor ) cuando construyes la nueva promesa de devolución. También participa en el subtipo de matrices (a través de ArraySpeciesCreate ).Fuera del lenguaje en sí, a veces las personas lo usan cuando intentan construir funciones genéricas de "clonación" o simplemente cuando quieren 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 las personas lo usan.
Está allí por defecto, solo necesita volver a colocarlo cuando reemplaza el objeto en la
prototype
propiedad de una función :Si no haces esto:
... luego
Student.prototype.constructor
hereda de loPerson.prototype
que (presumiblemente) tieneconstructor = Person
. Entonces es engañoso. Y, por supuesto, si está subclasificando algo que lo usa (comoPromise
oArray
) y no usaclass
¹ (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 biblioteca que usa) lo usa. Siempre me he asegurado de que esté correctamente conectado.
Por supuesto, con la
class
palabra clave ES2015 (también conocida como ES6) , la mayoría de las veces la hubiéramos usado, ya no tenemos que hacerlo, porque se maneja por nosotros cuando lo hacemos.¹ "... si estás subclasificando algo que lo usa (como
Promise
oArray
) y no estás usandoclass
..." - Es posible hacer eso, pero es un verdadero dolor (y un poco tonto). Tienes que usarReflect.construct
.fuente
TLDR; No es súper necesario, pero probablemente ayudará a largo plazo, y es más preciso hacerlo.
NOTA: Mucho editado ya que mi respuesta anterior fue escrita de manera confusa y tenía algunos errores que no tuve en mi prisa por responder. Gracias a quienes señalaron algunos errores atroces.
Básicamente, es conectar subclases correctamente en Javascript. Cuando subclasemos, tenemos que hacer algunas cosas extrañas para asegurarnos de que la delegación prototípica funcione correctamente, incluida la sobrescritura de un
prototype
objeto. Sobrescribir unprototype
objeto incluye elconstructor
, por lo que debemos corregir la referencia.Veamos rápidamente cómo funcionan las 'clases' en ES5.
Digamos que tiene una función de constructor y su prototipo:
Cuando llame al constructor para crear una instancia, diga
Adam
:La
new
palabra clave invocada con 'Persona' básicamente ejecutará el constructor Persona con algunas líneas de código adicionales:Si nosotros
console.log(adam.species)
, la búsqueda fallará en laadam
instancia, y buscamos la cadena prototípica en ella.prototype
, que esPerson.prototype
- yPerson.prototype
tiene una.species
propiedad, por lo que la búsqueda tendrá éxitoPerson.prototype
. Luego se registrará'human'
.Aquí, el
Person.prototype.constructor
apuntará correctamentePerson
.Así que ahora la parte interesante, la llamada 'subclase'. Si queremos crear una
Student
clase, que es una subclase de laPerson
clase con algunos cambios adicionales, tendremos que asegurarnos de queStudent.prototype.constructor
apunta a Student para mayor precisión.No hace esto por sí mismo. Cuando subclases, el código se ve así:
Llamar
new Student()
aquí devolvería un objeto con todas las propiedades que queremos. Aquí, si lo comprobamoseve instanceof Person
, volveríafalse
. Si intentamos accedereve.species
, volveríaundefined
.En otras palabras, necesitamos conectar la delegación para que
eve instanceof Person
devuelva verdadero y para que las instancias deStudent
delegar correctamenteStudent.prototype
, y luegoPerson.prototype
.PERO ya que lo estamos llamando con la
new
palabra clave, ¿recuerda lo que agrega esa invocación? LlamaríaObject.create(Student.prototype)
, que es cómo establecemos esa relación de delegación entreStudent
yStudent.prototype
. Tenga en cuenta que en este momento,Student.prototype
está vacío. Por lo tanto, buscar.species
una instancia deStudent
fallará ya que solo delegaStudent.prototype
, y el.species
propiedad no existe enStudent.prototype
.Cuando asignamos
Student.prototype
a sí mismoObject.create(Person.prototype)
,Student.prototype
luego delegamos aPerson.prototype
, y mirar hacia arribaeve.species
regresaráhuman
como esperamos. Presumiblemente nos gustaría que herede de Student.prototype AND Person.prototype. Entonces necesitamos arreglar todo eso.Ahora la delegación funciona, pero estamos sobrescribiendo
Student.prototype
con una dePerson.prototype
. Entonces, si llamamosStudent.prototype.constructor
, apuntaría a enPerson
lugar deStudent
. Es por eso que necesitamos arreglarlo.En ES5, nuestra
constructor
propiedad es una referencia que se refiere a una función que hemos escrito con la intención de ser un 'constructor'. Aparte de lo quenew
nos da la palabra clave, el constructor es una función 'simple'.En ES6,
constructor
ahora está integrado en la forma en que escribimos clases, como en, se proporciona como un método cuando declaramos una clase. Esto es simplemente azúcar sintáctico, pero nos otorga algunas comodidades como el acceso asuper
cuando estamos ampliando una clase existente. Entonces escribiríamos el código anterior de esta manera:fuente
eve instanceof Student
devueltotrue
. Consulte stackoverflow.com/questions/35537995/… para obtener una explicación. También cuando dices awhich is, at the moment, nothing
qué te refieres? Cada función tiene un prototipo, así que si verificoStudent.prototype
es algo.Object.create(Person.prototype)
, elStudent.prototype
está vacío. Entonces, si iniciamos sesióneve.species
, no delegará correctamente a su superclase, Persona, y no lo hará'human'
. Presumiblemente, queremos que cada subclase herede de su prototipo y también del prototipo de su súper.which is, at the moment, nothing
, quise decir que elStudent.prototype
objeto está vacío.Student.prototype
toObject.create(Person.prototype)
, que es, si recuerda, de la misma manera que todas las instancias de Person están configuradas para delegarPerson.prototype
, buscar una propiedad en una instancia deStudent
delegaría solo enStudent.prototype
. Entonceseve.species
fallará su búsqueda. Si lo asignamos,Student.prototype
se delega a sí mismoPerson.prototype
, y al buscareve.species
volveráhuman
.instance
el constructor 'subclase', será precisa". No,instanceof
no usaconstructor
. "Sin embargo, si buscamos el constructor del prototipo del estudiante, todavía apuntaría a Persona" No, lo seráStudent
. No entiendo el punto de este ejemplo. Llamar a una función en un constructor no es herencia. "En ES6, el constructor ahora es una función real en lugar de una referencia a una función" Uh, ¿qué?No estoy de acuerdo. No es necesario configurar el prototipo. Tome exactamente el mismo código pero elimine la línea prototype.constructor. ¿Algo cambia? No. Ahora, realice los siguientes cambios:
y al final del código de prueba ...
El color será azul.
Un cambio en el prototype.constructor, en mi experiencia, no hace mucho a menos que esté haciendo cosas muy específicas y muy complicadas que probablemente no sean una buena práctica de todos modos :)
Editar: después de hurgar un poco en la web y hacer un poco de experimentación, parece que las personas configuran el constructor para que 'se parezca' a lo que se está construyendo con 'nuevo'. Supongo que argumentaría que el problema con esto es que javascript es un lenguaje prototipo: no existe la herencia. Pero la mayoría de los programadores provienen de un entorno de programación que empuja la herencia como "el camino". Así que ideamos todo tipo de cosas para tratar de hacer de este lenguaje prototípico un lenguaje 'clásico' ... como extender las 'clases'. Realmente, en el ejemplo que dieron, un nuevo estudiante es una persona, no se está 'extendiendo' de otro estudiante ... el estudiante es todo acerca de la persona, y lo que sea la persona es el estudiante también. Extiende al estudiante, y lo que sea que tú '
Crockford está un poco loco y demasiado celoso, pero lee seriamente algunas de las cosas que ha escrito ... te hará ver estas cosas de manera muy diferente.
fuente
Esto tiene el gran obstáculo que si escribieras
pero si había un Maestro cuyo prototipo era también Persona y usted escribió
entonces el constructor de estudiantes es ahora maestro!
Editar: puede evitar esto asegurándose de haber configurado los prototipos de Alumno y Profesor con nuevas instancias de la clase Persona creada con Object.create, como en el ejemplo de Mozilla.
fuente
Student.prototype = Object.create(...)
se asume en esta pregunta. Esta respuesta no agrega nada más que una posible confusión.Object.create(...)
se usa en el artículo de MDN que generó la pregunta, pero no en la pregunta misma. Estoy seguro de que muchas personas no hacen clic.Hasta ahora la confusión sigue ahí.
Siguiendo el ejemplo original, ya que tiene un objeto existente
student1
como:Suponga que no quiere saber cómo
student1
se crea, solo quiere otro objeto como este, puede usar la propiedad constructor destudent1
like:Aquí no podrá obtener las propiedades
Student
si la propiedad del constructor no está establecida. Más bien creará unPerson
objeto.fuente
Obtuve un buen ejemplo de código de por qué es realmente necesario configurar el constructor prototipo
fuente
createNewCar
método es crear fábricas? Además, parece que debería haberse usado envar audiFactory = new CarFactory("Audi")
lugar de usar la herencia.this.constructor
internamente, por lo que no es sorprendente que tenga que configurarse. ¿Tienes algún ejemplo sin él?No hay necesidad de 'clases' de funciones azucaradas o de usar 'Nuevo' en estos días. Usa literales de objeto.
El prototipo de objeto ya es una 'clase'. Cuando define un objeto literal, ya es una instancia del Objeto prototipo. Estos también pueden actuar como el prototipo de otro objeto, etc.
Vale la pena leerlo :
fuente
Es necesario cuando necesita una alternativa a
toString
sin monkeypatching:fuente
foo.constructor()
??EDITAR, en realidad estaba equivocado. Comentar la salida no cambia su comportamiento en absoluto. (Lo probé)
Si es necesario. Cuando tu lo hagas
Student.prototype.constructor
se conviertePerson
. Por lo tanto, llamarStudent()
devolvería un objeto creado porPerson
. Si entonces lo hacesStudent.prototype.constructor
se restablece de nuevo aStudent
. Ahora, cuando se llamaStudent()
ejecutaStudent
, que llama al constructor principalParent()
, devuelve el objeto heredado correctamente. Si no reinicióStudent.prototype.constructor
antes de llamarlo, obtendría un objeto que no tendría ninguna de las propiedades establecidasStudent()
.fuente
Dada la función de constructor simple:
Por defecto (de la especificación https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/constructor ), todos los prototipos obtienen automáticamente una propiedad llamada constructor que apunta de nuevo a la función en que es una propiedad Dependiendo del constructor, se pueden agregar otras propiedades y métodos al prototipo, lo cual no es una práctica muy común, pero aún está permitido para extensiones.
Entonces, simplemente respondiendo: debemos asegurarnos de que el valor en prototype.constructor esté configurado correctamente como lo supone la especificación.
¿Tenemos que establecer siempre correctamente este valor? Ayuda con la depuración y hace que la estructura interna sea consistente con las especificaciones. Definitivamente deberíamos cuando nuestra API está siendo utilizada por terceros, pero no realmente cuando el código finalmente se ejecuta en tiempo de ejecución.
fuente
Aquí hay un ejemplo de MDN que encontré muy útil para comprender sus usos.
En JavaScript, tenemos el
async functions
que devuelve el objeto AsyncFunction .AsyncFunction
no es un objeto global, pero uno puede recuperarlo usando laconstructor
propiedad y utilizarlo.fuente
No es necesario. Es solo una de las muchas cosas tradicionales que hacen los campeones de OOP para tratar de convertir la herencia prototípica de JavaScript en herencia clásica. Lo único que lo siguiente
sí, es que ahora tienes una referencia del "constructor" actual.
En la respuesta de Wayne, que se ha marcado como correcta, podría exactamente lo mismo que hace el siguiente código
con el código a continuación (solo reemplace this.constructor con Person)
Gracias a Dios que con los ES6, los puristas de la herencia clásica pueden usar los operadores nativos del lenguaje como class, extend y super y no tenemos que ver como prototype.constructor correcciones y referencias de padres.
fuente