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.constructorapuntaráparent_classsi 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
Personclase base . Me gusta esto:¿Qué sucede cuando creamos uno nuevo
Studenty 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 unaStudentcopia de la clase "base". Solo podemos devolver aPerson. Sin embargo, si hubiéramos reiniciado el constructor:... entonces todo funciona como se esperaba:
fuente
constructoratributo 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áticamenteconstructorenf.prototypecada vez que se declara una funciónf. Sin embargo, se puede sobrescribir en cualquier momento.constructorsignifica 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.constructores laStudentfunción (porque la configuró conStudent.prototype.constructor = Student;), lacopyfunción termina llamando a laStudentfunción. No estoy seguro de cuál fue su intención con el//just as badcomentario.Studentconstructor hubiera agregado un argumento adicional comoStudent(name, id):? ¿Entonces tenemos que anular lacopyfunción, llamar a laPersonversión desde dentro y luego copiar laidpropiedad adicional ?Si y no.
En ES5 y versiones anteriores, el JavaScript en sí no se usaba
constructorpara nada. Definió que el objeto predeterminado en laprototypepropiedad 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#thenusa laconstructorpropiedad 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
prototypepropiedad de una función :Si no haces esto:
... luego
Student.prototype.constructorhereda de loPerson.prototypeque (presumiblemente) tieneconstructor = Person. Entonces es engañoso. Y, por supuesto, si está subclasificando algo que lo usa (comoPromiseoArray) 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
classpalabra 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
PromiseoArray) 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
prototypeobjeto. Sobrescribir unprototypeobjeto 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
newpalabra 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 laadaminstancia, y buscamos la cadena prototípica en ella.prototype, que esPerson.prototype- yPerson.prototypetiene una.speciespropiedad, por lo que la búsqueda tendrá éxitoPerson.prototype. Luego se registrará'human'.Aquí, el
Person.prototype.constructorapuntará correctamentePerson.Así que ahora la parte interesante, la llamada 'subclase'. Si queremos crear una
Studentclase, que es una subclase de laPersonclase con algunos cambios adicionales, tendremos que asegurarnos de queStudent.prototype.constructorapunta 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 Persondevuelva verdadero y para que las instancias deStudentdelegar correctamenteStudent.prototype, y luegoPerson.prototype.PERO ya que lo estamos llamando con la
newpalabra clave, ¿recuerda lo que agrega esa invocación? LlamaríaObject.create(Student.prototype), que es cómo establecemos esa relación de delegación entreStudentyStudent.prototype. Tenga en cuenta que en este momento,Student.prototypeestá vacío. Por lo tanto, buscar.speciesuna instancia deStudentfallará ya que solo delegaStudent.prototype, y el.speciespropiedad no existe enStudent.prototype.Cuando asignamos
Student.prototypea sí mismoObject.create(Person.prototype),Student.prototypeluego delegamos aPerson.prototype, y mirar hacia arribaeve.speciesregresaráhumancomo 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.prototypecon una dePerson.prototype. Entonces, si llamamosStudent.prototype.constructor, apuntaría a enPersonlugar deStudent. Es por eso que necesitamos arreglarlo.En ES5, nuestra
constructorpropiedad es una referencia que se refiere a una función que hemos escrito con la intención de ser un 'constructor'. Aparte de lo quenewnos da la palabra clave, el constructor es una función 'simple'.En ES6,
constructorahora 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 asupercuando estamos ampliando una clase existente. Entonces escribiríamos el código anterior de esta manera:fuente
eve instanceof Studentdevueltotrue. Consulte stackoverflow.com/questions/35537995/… para obtener una explicación. También cuando dices awhich is, at the moment, nothingqué te refieres? Cada función tiene un prototipo, así que si verificoStudent.prototypees algo.Object.create(Person.prototype), elStudent.prototypeestá 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.prototypeobjeto está vacío.Student.prototypetoObject.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 deStudentdelegaría solo enStudent.prototype. Entonceseve.speciesfallará su búsqueda. Si lo asignamos,Student.prototypese delega a sí mismoPerson.prototype, y al buscareve.speciesvolveráhuman.instanceel constructor 'subclase', será precisa". No,instanceofno 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
student1como:Suponga que no quiere saber cómo
student1se crea, solo quiere otro objeto como este, puede usar la propiedad constructor destudent1like:Aquí no podrá obtener las propiedades
Studentsi la propiedad del constructor no está establecida. Más bien creará unPersonobjeto.fuente
Obtuve un buen ejemplo de código de por qué es realmente necesario configurar el constructor prototipo
fuente
createNewCarmétodo es crear fábricas? Además, parece que debería haberse usado envar audiFactory = new CarFactory("Audi")lugar de usar la herencia.this.constructorinternamente, 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
toStringsin 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.constructorse conviertePerson. Por lo tanto, llamarStudent()devolvería un objeto creado porPerson. Si entonces lo hacesStudent.prototype.constructorse restablece de nuevo aStudent. Ahora, cuando se llamaStudent()ejecutaStudent, que llama al constructor principalParent(), devuelve el objeto heredado correctamente. Si no reinicióStudent.prototype.constructorantes 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 functionsque devuelve el objeto AsyncFunction .AsyncFunctionno es un objeto global, pero uno puede recuperarlo usando laconstructorpropiedad 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