¿Por qué es necesario configurar el constructor prototipo?

294

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?

noveno
fuente
23
Me alegra que hayas preguntado esto: ayer leí la misma documentación y tenía curiosidad sobre el razonamiento detrás de la configuración explícita del constructor también.
Wylie
66
Solo tenía que señalar esto, ¡esta pregunta ahora está vinculada en el artículo que vinculaste!
Marie
77
nada es necesario
nothingisnecessary
1
El subclass.prototype.constructorapuntará parent_classsi no escribes subclass.prototype.constructor = subclass; Es decir, usar subclass.prototype.constructor()directamente producirá un resultado inesperado.
KuanYu Chu

Respuestas:

263

No siempre es necesario, pero tiene sus usos. Supongamos que queremos hacer un método de copia en la Personclase base . Me gusta 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);

¿Qué sucede cuando creamos uno nuevo Studenty 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 verificaciones explícitas), no tendríamos forma de devolver una Studentcopia de la clase "base". Solo podemos devolver a 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
Wayne
fuente
34
Nota: El constructoratributo no tiene ningún significado especial en JS, por lo que también podría llamarlo bananashake. La única diferencia es que el motor se inicializa automáticamente constructoren f.prototypecada vez que se declara una función f. Sin embargo, se puede sobrescribir en cualquier momento.
user123444555621
58
@ Pumbaa80 - Me obtener su punto, pero el hecho de que el motor se inicializa automáticamente constructorsignifica que lo hace tener un significado especial en JS, casi por definición.
Wayne
13
Solo quiero aclarar que la razón por la cual el comportamiento que dijiste funciona es porque lo usas en return new this.constructor(this.name);lugar de hacerlo return new Person(this.name);. Como this.constructores la Studentfunción (porque la configuró con Student.prototype.constructor = Student;), la copyfunción termina llamando a la Studentfunción. No estoy seguro de cuál fue su intención con el //just as badcomentario.
CEGRD
12
@lwburk, ¿qué quieres decir con "// igual de malo"?
CEGRD
66
Creo que lo entiendo. Pero, ¿y si el Studentconstructor hubiera agregado un argumento adicional como Student(name, id):? ¿Entonces tenemos que anular la copyfunción, llamar a la Personversión desde dentro y luego copiar la idpropiedad adicional ?
snapfractalpop
76

¿Sirve esto para algún propósito importante?

Si y no.

En ES5 y versiones anteriores, el JavaScript en sí no se usaba constructorpara nada. Definió que el objeto predeterminado en la prototypepropiedad 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 la constructorpropiedad 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á bien omitirlo?

Está allí por defecto, solo necesita volver a colocarlo cuando reemplaza el objeto en la prototypepropiedad de una función :

Student.prototype = Object.create(Person.prototype);

Si no haces esto:

Student.prototype.constructor = Student;

... luego Student.prototype.constructorhereda de lo Person.prototypeque (presumiblemente) tiene constructor = Person. Entonces es engañoso. Y, por supuesto, si está subclasificando algo que lo usa (como Promiseo 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 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.

class Student extends Person {
}

¹ "... si estás subclasificando algo que lo usa (como Promiseo Array) y no estás usando class..."  - Es posible hacer eso, pero es un verdadero dolor (y un poco tonto). Tienes que usar Reflect.construct.

TJ Crowder
fuente
12

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 un prototypeobjeto incluye el constructor, 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:

//Constructor Function
var Person = function(name, age) {
  this.name = name;
  this.age = age;
}

//Prototype Object - shared between all instances of Person
Person.prototype = {
  species: 'human',
}

Cuando llame al constructor para crear una instancia, diga Adam:

// instantiate using the 'new' keyword
var adam = new Person('Adam', 19);

La newpalabra clave invocada con 'Persona' básicamente ejecutará el constructor Persona con algunas líneas de código adicionales:

function Person (name, age) {
  // This additional line is automatically added by the keyword 'new'
  // it sets up the relationship between the instance and the prototype object
  // So that the instance will delegate to the Prototype object
  this = Object.create(Person.prototype);

  this.name = name;
  this.age = age;

  return this;
}

/* So 'adam' will be an object that looks like this:
 * {
 *   name: 'Adam',
 *   age: 19
 * }
 */

Si nosotros console.log(adam.species), la búsqueda fallará en la adaminstancia, y buscamos la cadena prototípica en ella .prototype, que esPerson.prototype - y Person.prototype tiene una .speciespropiedad, por lo que la búsqueda tendrá éxito Person.prototype. Luego se registrará 'human'.

Aquí, el Person.prototype.constructorapuntará correctamente Person.

Así que ahora la parte interesante, la llamada 'subclase'. Si queremos crear una Studentclase, que es una subclase de la Personclase con algunos cambios adicionales, tendremos que asegurarnos de que Student.prototype.constructorapunta a Student para mayor precisión.

No hace esto por sí mismo. Cuando subclases, el código se ve así:

var Student = function(name, age, school) {
 // Calls the 'super' class, as every student is an instance of a Person
 Person.call(this, name, age);
 // This is what makes the Student instances different
 this.school = school
}

var eve = new Student('Eve', 20, 'UCSF');

console.log(Student.prototype); // this will be an empty object: {}

Llamar new Student()aquí devolvería un objeto con todas las propiedades que queremos. Aquí, si lo comprobamos eve instanceof Person, volveríafalse . Si intentamos acceder eve.species, volvería undefined.

En otras palabras, necesitamos conectar la delegación para que eve instanceof Persondevuelva verdadero y para que las instancias de Studentdelegar correctamenteStudent.prototype , y luego Person.prototype.

PERO ya que lo estamos llamando con la newpalabra clave, ¿recuerda lo que agrega esa invocación? Llamaría Object.create(Student.prototype), que es cómo establecemos esa relación de delegación entre Studenty Student.prototype. Tenga en cuenta que en este momento, Student.prototypeestá vacío. Por lo tanto, buscar .speciesuna instancia de Studentfallará ya que solo delega Student.prototype, y el.species propiedad no existe en Student.prototype.

Cuando asignamos Student.prototypea sí mismo Object.create(Person.prototype), Student.prototypeluego delegamos a Person.prototype, y mirar hacia arriba eve.speciesregresará humancomo esperamos. Presumiblemente nos gustaría que herede de Student.prototype AND Person.prototype. Entonces necesitamos arreglar todo eso.

/* This sets up the prototypal delegation correctly 
 *so that if a lookup fails on Student.prototype, it would delegate to Person's .prototype
 *This also allows us to add more things to Student.prototype 
 *that Person.prototype may not have
 *So now a failed lookup on an instance of Student 
 *will first look at Student.prototype, 
 *and failing that, go to Person.prototype (and failing /that/, where do we think it'll go?)
*/
Student.prototype = Object.create(Person.prototype);

Ahora la delegación funciona, pero estamos sobrescribiendo Student.prototypecon una de Person.prototype. Entonces, si llamamos Student.prototype.constructor, apuntaría a en Personlugar de Student. Es por eso que necesitamos arreglarlo.

// Now we fix what the .constructor property is pointing to    
Student.prototype.constructor = Student

// If we check instanceof here
console.log(eve instanceof Person) // true

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 que newnos 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 a supercuando estamos ampliando una clase existente. Entonces escribiríamos el código anterior de esta manera:

class Person {
  // constructor function here
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }
  // static getter instead of a static property
  static get species() {
    return 'human';
  }
}

class Student extends Person {
   constructor(name, age, school) {
      // calling the superclass constructor
      super(name, age);
      this.school = school;
   }
}
bthehuman
fuente
eve instanceof Studentdevuelto true. Consulte stackoverflow.com/questions/35537995/… para obtener una explicación. También cuando dices a which is, at the moment, nothingqué te refieres? Cada función tiene un prototipo, así que si verifico Student.prototypees algo.
Aseem Bansal
Mi error. Debería haber leído 'eve instanceof Person' que devolvería falso. Enmendaré esa parte. Tiene razón en que cada función tiene una propiedad prototipo. Sin embargo , sin asignar el prototipo a Object.create(Person.prototype), el Student.prototypeestá vacío. Entonces, si iniciamos sesión eve.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.
bthehuman
Para aclarar, por which is, at the moment, nothing, quise decir que el Student.prototypeobjeto está vacío.
bthehuman
Más sobre el prototipo: sin la asignación de Student.prototypeto Object.create(Person.prototype), que es, si recuerda, de la misma manera que todas las instancias de Person están configuradas para delegar Person.prototype, buscar una propiedad en una instancia de Studentdelegaría solo en Student.prototype . Entonces eve.speciesfallará su búsqueda. Si lo asignamos, Student.prototypese delega a sí mismo Person.prototype, y al buscar eve.speciesvolverá human.
bthehuman
Parece que hay algunas cosas mal aquí: "Es necesario cuando se intenta emular 'subclases' [...] para que cuando verifique si una instancia es instanceel constructor 'subclase', será precisa". No, instanceofno usa constructor. "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é?
Felix Kling
10

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:

Person = function () {
    this.favoriteColor = 'black';
}

Student = function () {
    Person.call(this);
    this.favoriteColor = 'blue';
}

y al final del código de prueba ...

alert(student1.favoriteColor);

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.

Stephen
fuente
8
Esto no hereda la cadena prototipo.
Cypher
1
@Cypher aplauso lento bienvenido a la conversación, cuatro años después. Sí, la cadena del prototipo se hereda, independientemente de si sobrescribe el prototype.constructor. Intenta probarlo.
Stephen
77
Te falta el código que hereda el prototipo. Bienvenido al internet.
Cypher
1
El fragmento de código @Cypher se basó en el código del artículo vinculado. Bienvenido a leer la pregunta en su totalidad. Oh. Espere.
Stephen
1
@macher lo dije como herencia clásica. Mala elección de la redacción de mi parte.
Stephen
9

Esto tiene el gran obstáculo que si escribieras

Student.prototype.constructor = Student;

pero si había un Maestro cuyo prototipo era también Persona y usted escribió

Teacher.prototype.constructor = Teacher;

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.

Student.prototype = Object.create(Person.prototype);
Teacher.prototype = Object.create(Person.prototype);
James D
fuente
1
Student.prototype = Object.create(...)se asume en esta pregunta. Esta respuesta no agrega nada más que una posible confusión.
André Chalella
3
@ AndréNeves Me pareció útil esta respuesta. 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.
Alex Ross el
El artículo vinculado al que se hace referencia en la pregunta ya usa Object.create (). Esta respuesta y la edición de la respuesta no son realmente relevantes, y es confuso por decir lo menos :-)
Drenai
1
El punto más amplio es que hay problemas que atraparán a las personas nuevas en los prototipos de Javascript. Si estamos discutiendo en 2016, entonces realmente debería usar clases ES6, Babel y / o mecanografiado. Pero si realmente desea construir clases manualmente de esta manera, es útil comprender cómo funcionan realmente las cadenas de prototipos para aprovechar su poder. Puede utilizar cualquier objeto como prototipo, y tal vez no desee crear un objeto nuevo por separado. Además, antes de que HTML 5 se extendiera por completo, Object.create no siempre estaba disponible, por lo que era más fácil configurar una clase de forma incorrecta.
James D
5

Hasta ahora la confusión sigue ahí.

Siguiendo el ejemplo original, ya que tiene un objeto existente student1como:

var student1 = new Student("Janet", "Applied Physics");

Suponga que no quiere saber cómo student1se crea, solo quiere otro objeto como este, puede usar la propiedad constructor de student1like:

var student2 = new student1.constructor("Mark", "Object-Oriented JavaScript");

Aquí no podrá obtener las propiedades Studentsi la propiedad del constructor no está establecida. Más bien creará un Personobjeto.

Mahavir
fuente
2

Obtuve un buen ejemplo de código de por qué es realmente necesario configurar el constructor prototipo

function CarFactory(name){ 
   this.name=name;  
} 
CarFactory.prototype.CreateNewCar = function(){ 
    return new this.constructor("New Car "+ this.name); 
} 
CarFactory.prototype.toString=function(){ 
    return 'Car Factory ' + this.name;
} 

AudiFactory.prototype = new CarFactory();      // Here's where the inheritance occurs 
AudiFactory.prototype.constructor=AudiFactory;       // Otherwise instances of Audi would have a constructor of Car 

function AudiFactory(name){ 
    this.name=name;
} 

AudiFactory.prototype.toString=function(){ 
    return 'Audi Factory ' + this.name;
} 

var myAudiFactory = new AudiFactory('');
  alert('Hay your new ' + myAudiFactory + ' is ready.. Start Producing new audi cars !!! ');            

var newCar =  myAudiFactory.CreateNewCar(); // calls a method inherited from CarFactory 
alert(newCar); 

/*
Without resetting prototype constructor back to instance, new cars will not come from New Audi factory, Instead it will come from car factory ( base class )..   Dont we want our new car from Audi factory ???? 
*/
usuario3877965
fuente
¿Tu createNewCarmétodo es crear fábricas? Además, parece que debería haberse usado en var audiFactory = new CarFactory("Audi")lugar de usar la herencia.
Bergi
Su ejemplo se usa this.constructorinternamente, por lo que no es sorprendente que tenga que configurarse. ¿Tienes algún ejemplo sin él?
Dmitri Zaitsev
1

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.

const Person = {
  name: '[Person.name]',
  greeting: function() {
    console.log( `My name is ${ this.name || '[Name not assigned]' }` );
  }
};
// Person.greeting = function() {...} // or define outside the obj if you must

// Object.create version
const john = Object.create( Person );
john.name = 'John';
console.log( john.name ); // John
john.greeting(); // My name is John 
// Define new greeting method
john.greeting = function() {
    console.log( `Hi, my name is ${ this.name }` )
};
john.greeting(); // Hi, my name is John

// Object.assign version
const jane = Object.assign( Person, { name: 'Jane' } );
console.log( jane.name ); // Jane
// Original greeting
jane.greeting(); // My name is Jane 

// Original Person obj is unaffected
console.log( Person.name ); // [Person.name]
console.log( Person.greeting() ); // My name is [Person.name]

Vale la pena leerlo :

Los lenguajes orientados a objetos basados ​​en clases, como Java y C ++, se basan en el concepto de dos entidades distintas: clases e instancias.

...

Un lenguaje basado en prototipos, como JavaScript, no hace esta distinción: simplemente tiene objetos. Un lenguaje basado en prototipos tiene la noción de un objeto prototípico, un objeto utilizado como plantilla a partir del cual obtener las propiedades iniciales de un nuevo objeto. Cualquier objeto puede especificar sus propias propiedades, ya sea cuando lo cree o en tiempo de ejecución. Además, cualquier objeto puede asociarse como prototipo para otro objeto, permitiendo que el segundo objeto comparta las propiedades del primer objeto.

ucsarge
fuente
1

Es necesario cuando necesita una alternativa a toStringsin monkeypatching:

//Local
foo = [];
foo.toUpperCase = String(foo).toUpperCase;
foo.push("a");
foo.toUpperCase();

//Global
foo = [];
window.toUpperCase = function (obj) {return String(obj).toUpperCase();}
foo.push("a");
toUpperCase(foo);

//Prototype
foo = [];
Array.prototype.toUpperCase = String.prototype.toUpperCase;
foo.push("a");
foo.toUpperCase();

//toString alternative via Prototype constructor
foo = [];
Array.prototype.constructor = String.prototype.toUpperCase;
foo.push("a,b");
foo.constructor();

//toString override
var foo = [];
foo.push("a");
var bar = String(foo);
foo.toString = function() { return bar.toUpperCase(); }
foo.toString();

//Object prototype as a function
Math.prototype = function(char){return Math.prototype[char]};
Math.prototype.constructor = function() 
  {
  var i = 0, unicode = {}, zero_padding = "0000", max = 9999;
  
  while (i < max) 
    {
    Math.prototype[String.fromCharCode(parseInt(i, 16))] = ("u" + zero_padding + i).substr(-4);

    i = i + 1;
    }    
  }

Math.prototype.constructor();
console.log(Math.prototype("a") );
console.log(Math.prototype["a"] );
console.log(Math.prototype("a") === Math.prototype["a"]);

Paul Sweatte
fuente
¿Qué se supone que debe hacer todo esto? foo.constructor()??
Ry-
0

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 = new Person();  

Student.prototype.constructorse convierte Person. Por lo tanto, llamar Student()devolvería un objeto creado por Person. Si entonces lo haces

Student.prototype.constructor = Student; 

Student.prototype.constructorse restablece de nuevo a Student. Ahora, cuando se llama Student()ejecuta Student, que llama al constructor principal Parent(), 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 establecidas Student().

bob invisible
fuente
3
La construcción del prototipo puede convertirse en una persona, pero eso es apropiado ya que hereda todas las propiedades y métodos de la Persona. Crear un nuevo Student () sin establecer el prototype.constructor apropiadamente llama a su propio constructor.
Stephen
0

Dada la función de constructor simple:

function Person(){
    this.name = 'test';
}


console.log(Person.prototype.constructor) // function Person(){...}

Person.prototype = { //constructor in this case is Object
    sayName: function(){
        return this.name;
    }
}

var person = new Person();
console.log(person instanceof Person); //true
console.log(person.sayName()); //test
console.log(Person.prototype.constructor) // function Object(){...}

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.

kospiotr
fuente
0

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 la constructorpropiedad y utilizarlo.

function resolveAfter2Seconds(x) {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve(x);
    }, 2000);
  });
}

// AsyncFunction constructor
var AsyncFunction = Object.getPrototypeOf(async function(){}).constructor

var a = new AsyncFunction('a', 
                          'b', 
                          'return await resolveAfter2Seconds(a) + await resolveAfter2Seconds(b);');

a(10, 20).then(v => {
  console.log(v); // prints 30 after 4 seconds
});
Hitesh Kumar
fuente
-1

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

Student.prototype.constructor = Student; 

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

Person.prototype.copy = function() {  
    // return new Person(this.name); // just as bad
    return new this.constructor(this.name);
};  

con el código a continuación (solo reemplace this.constructor con Person)

Person.prototype.copy = function() {  
    // return new Person(this.name); // just as bad
    return new Person(this.name);
}; 

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.

Roumelis George
fuente