Por que é necessário definir o construtor do protótipo?

294

Na seção sobre herança no artigo MDN Introdução ao Javascript Orientado a Objetos , notei que eles definiram o prototype.constructor:

// correct the constructor pointer because it points to Person
Student.prototype.constructor = Student;  

Isso serve a algum propósito importante? Está tudo bem em omitir?

trinto
fonte
23
Que bom que você perguntou isso: li a mesma documentação ontem e fiquei curioso sobre o raciocínio por trás da definição explícita do construtor também.
Wylie
6
Eu apenas tive que salientar isso, esta questão agora está vinculada no artigo que você vinculou!
Marie
7
nada é necessário
nothingisnecessary
1
O subclass.prototype.constructorapontará para parent_classse você não escrever subclass.prototype.constructor = subclass; Ou seja, o uso subclass.prototype.constructor()direto produzirá um resultado inesperado.
KuanYu Chu 16/05/19

Respostas:

263

Nem sempre é necessário, mas tem seus usos. Suponha que desejássemos fazer um método de cópia na Personclasse base . Como isso:

// 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);

Agora, o que acontece quando criamos um novo Studente o copiamos?

var student1 = new Student("trinth");  
console.log(student1.copy() instanceof Student); // => false

A cópia não é uma instância de Student. Isso ocorre porque (sem verificações explícitas), não teríamos como retornar uma Studentcópia da classe "base". Nós podemos somente retornar a Person. No entanto, se tivéssemos reiniciado o construtor:

// correct the constructor pointer because it points to Person  
Student.prototype.constructor = Student;

... então tudo funciona como esperado:

var student1 = new Student("trinth");  
console.log(student1.copy() instanceof Student); // => true
Wayne
fonte
34
Nota: O constructoratributo não tem nenhum significado especial em JS, portanto, é possível chamá-lo bananashake. A única diferença é que o motor automaticamente inicializa constructorem f.prototypequando você declara uma função f. No entanto, pode ser substituído a qualquer momento.
user123444555621
58
@ Pumbaa80 - Eu entendo seu ponto, mas o fato de que o motor automaticamente inicializa constructorsignifica que ele faz tem significado especial em JS, praticamente por definição.
Wayne
13
Eu só quero esclarecer que a razão pela qual o comportamento que você disse funciona é porque você usa em return new this.constructor(this.name);vez de return new Person(this.name);. Como this.constructoré a Studentfunção (porque você a define Student.prototype.constructor = Student;), a copyfunção acaba chamando a Studentfunção. Não tenho certeza de qual era sua intenção com o //just as badcomentário.
CEGRD 07/12/12
12
@lwburk, o que você quer dizer com "// tão ruim"?
CEGRD 13/12/12
6
Eu acho que entendi. Mas, e se o Studentconstrutor tivesse adicionado um argumento adicional como Student(name, id):? Precisamos substituir a copyfunção, chamando a Personversão de dentro dela e também copiando a idpropriedade adicional ?
snapfractalpop
76

Isso serve a algum propósito importante?

Sim e não.

No ES5 e versões anteriores, o próprio JavaScript não usava constructorpara nada. Ele definiu que o objeto padrão na prototypepropriedade de uma função o teria e que se referiria à função, e foi isso . Nada mais na especificação se refere a isso.

Isso mudou no ES2015 (ES6), que começou a usá-lo em relação às hierarquias de herança. Por exemplo, Promise#thenusa a constructorpropriedade da promessa que você chama (via SpeciesConstructor ) ao criar a nova promessa de retorno. Também está envolvido na subtipagem de matrizes (via ArraySpeciesCreate ).

Fora da linguagem em si, às vezes as pessoas a usavam quando tentavam criar funções genéricas de "clone" ou geralmente quando queriam se referir ao que acreditavam ser a função construtora do objeto. Minha experiência é que usá-lo é raro, mas às vezes as pessoas o usam.

Está tudo bem em omitir?

Está lá por padrão, você só precisa colocá-lo novamente quando substituir o objeto na prototypepropriedade de uma função :

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

Se você não fizer isso:

Student.prototype.constructor = Student;

... então Student.prototype.constructorherda da Person.prototypequal (presumivelmente) tem constructor = Person. Então é enganoso. E, claro, se você estiver subclassificando algo que o usa (como Promiseou Array) e não usando class¹ (que lida com isso para você), convém garantir que você o configure corretamente. Então, basicamente: é uma boa ideia.

Tudo bem se nada no seu código (ou código de biblioteca que você usa) o usa. Eu sempre garanti que estava corretamente conectado.

Obviamente, com a classpalavra-chave do ES2015 (também conhecida como ES6) , na maioria das vezes a teríamos usado, não precisamos mais, porque ela é tratada para nós quando o fazemos

class Student extends Person {
}

¹ "... se você subclasse algo que o utiliza (como Promiseou Array) e não usando class..."  - É possível fazer isso, mas é uma dor real (e um pouco bobo). Você tem que usar Reflect.construct.

TJ Crowder
fonte
12

TLDR; Não é necessário, mas provavelmente ajudará a longo prazo, e é mais preciso fazê-lo.

OBSERVAÇÃO: Muito editada como minha resposta anterior foi confusa e tinha alguns erros que eu perdi na pressa em responder. Obrigado a quem apontou alguns erros flagrantes.

Basicamente, é para ligar subclasses corretamente em Javascript. Quando subclasses, temos que fazer algumas coisas descoladas para garantir que a delegação prototípica funcione corretamente, incluindo a substituição de um prototypeobjeto. A substituição de um prototypeobjeto inclui oconstructor , então precisamos corrigir a referência.

Vamos ver rapidamente como as 'classes' no ES5 funcionam.

Digamos que você tenha uma função construtora e seu protótipo:

//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',
}

Quando você chamar o construtor para instanciar, diga Adam:

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

A newpalavra-chave invocada com 'Person' basicamente executará o construtor Person com algumas linhas de código adicionais:

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
 * }
 */

Se nós console.log(adam.species), a pesquisa falharmos na adaminstância, e procurar a cadeia de protótipos na sua .prototype, que é Person.prototype- e Person.prototype tem uma .speciespropriedade, para que a pesquisa seja bem-sucedida Person.prototype. Em seguida, ele será registrado 'human'.

Aqui, o Person.prototype.constructorapontará corretamente para Person.

Então agora a parte interessante, a chamada 'subclasse'. Se queremos criar uma Studentturma, que é uma subclasse da Personturma com algumas alterações adicionais, precisamos garantir que os Student.prototype.constructorpontos apontem para precisão do aluno.

Não faz isso por si só. Quando você subclasse, o código fica assim:

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: {}

Ligar new Student()aqui retornaria um objeto com todas as propriedades que queremos. Aqui, se checarmos eve instanceof Person, ele retornaria false. Se tentarmos acessar eve.species, ele retornará undefined.

Em outras palavras, precisamos conectar a delegação para que eve instanceof Personretorne true e para que instâncias de Studentdelegar corretamente para Student.prototypee depois Person.prototype.

MAS, como a chamamos com a newpalavra - chave, lembre-se do que essa invocação adiciona? Isso chamaria Object.create(Student.prototype), e é assim que estabelecemos esse relacionamento delegacional entre Studente Student.prototype. Observe que, no momento, Student.prototypeestá vazio. Portanto, procurar .speciesuma instância de Studentfalharia, pois delega apenas Student.prototype , e a .speciespropriedade não existe Student.prototype.

Quando atribuímos Student.prototypea Object.create(Person.prototype), Student.prototypeele próprio delega e Person.prototype, olhando para cima eve.species, retornará humancomo esperamos. Presumivelmente, gostaríamos que herdasse do Student.prototype AND Person.prototype. Então, precisamos consertar tudo isso.

/* 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);

Agora a delegação funciona, mas estamos substituindo Student.prototypepor um de Person.prototype. Então, se ligarmos Student.prototype.constructor, apontaria para em Personvez de Student. Este é por isso que precisamos corrigi-lo.

// 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

No ES5, nossa constructorpropriedade é uma referência que se refere a uma função que escrevemos com a intenção de ser um 'construtor'. Além do que a newpalavra - chave nos fornece, o construtor é uma função "simples".

No ES6, o constructoragora está incorporado na maneira como escrevemos classes - como no, é fornecido como um método quando declaramos uma classe. Isso é simplesmente açúcar sintático, mas nos concede algumas conveniências, como o acesso a um superquando estamos estendendo uma classe existente. Então, escreveríamos o código acima dessa maneira:

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
fonte
eve instanceof Studentretornado true. Consulte stackoverflow.com/questions/35537995/… para obter explicações. Além disso, quando você diz a which is, at the moment, nothingque está se referindo? Toda função tem um protótipo, portanto, se eu verificar Student.prototype, é algo.
Aseem Bansal
Meu erro. Deveria ter lido 'eve instanceof Person' que retornaria false. Vou alterar essa parte. Você está certo de que toda função tem uma propriedade prototype. No entanto , sem atribuir o protótipo Object.create(Person.prototype), o Student.prototypeestá vazio. Portanto, se registrarmos eve.species, ele não delegará adequadamente até sua superclasse, Person, e não registrará 'human'. Presumivelmente, queremos que todas as subclasses herdem de seu protótipo e também do super.
bthehuman
Para esclarecer, which is, at the moment, nothingeu quis dizer que o Student.prototypeobjeto está vazio.
bthehuman
Mais sobre o protótipo: sem a atribuição de Student.prototypepara Object.create(Person.prototype)- que é, se você se lembrar, da mesma maneira que todas as instâncias de Person são configuradas para delegar Person.prototype-, procurar uma propriedade em uma instância de Studentdelegaria apenas Student.prototype . Então eve.species, falhará na pesquisa. Se o atribuirmos, ele Student.prototypepróprio delega para Person.prototype, e olhando para cima eve.speciesretornará human.
bthehuman
Parece que há algumas coisas erradas aqui: "É necessário quando você está tentando emular 'subclassing' [...] para que, quando você verificar se uma instância é instanceo Construtor '' subclasse '', será preciso." Não, instanceofnão usa constructor. "No entanto, se procurarmos o .prototype.constructor do aluno, ele ainda apontaria para Person" Não, será Student. Eu não entendo o objetivo deste exemplo. Chamar uma função em um construtor não é herança. "No ES6, o construtor agora é uma função real, em vez de uma referência a uma função" Uh what?
Felix Kling 27/02
10

Eu discordo. Não é necessário definir o protótipo. Pegue exatamente o mesmo código, mas remova a linha prototype.constructor. Alguma coisa muda? Não. Agora, faça as seguintes alterações:

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

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

e no final do código de teste ...

alert(student1.favoriteColor);

A cor será azul.

Uma mudança no prototype.constructor, na minha experiência, não faz muito, a menos que você esteja fazendo coisas muito específicas e muito complicadas que provavelmente não são boas práticas :)

Edit: Depois de bisbilhotar um pouco a web e fazer algumas experimentações, parece que as pessoas definem o construtor para que 'pareça' com a coisa que está sendo construída com 'novo'. Eu acho que argumentaria que o problema é que o javascript é uma linguagem de protótipo - não existe herança. Mas a maioria dos programadores vem de um plano de programação que leva a herança como 'o caminho'. Então, nós criamos todo tipo de coisa para tentar tornar essa linguagem prototípica uma linguagem "clássica" ... como estender "classes". Realmente, no exemplo que eles deram, um novo aluno é uma pessoa - não está 'estendendo-se' de outro aluno. O aluno é todo sobre a pessoa, e qualquer que seja a pessoa que o aluno também é. Estenda o aluno e o que você quiser

Crockford é um pouco louco e muito zeloso, mas faça algumas leituras sérias sobre algumas das coisas que ele escreveu. Isso fará com que você olhe essas coisas de maneira muito diferente.

Stephen
fonte
8
Isso não herda a cadeia de protótipos.
cifra
1
Cypher bate palmas bem-vindo à conversa, quatro anos depois. Sim, a cadeia de protótipos é herdada, independentemente de você substituir o prototype.constructor. Tente testá-lo.
Stephen
7
Está faltando o código que herda o protótipo. Bem-vindo à internet.
cifra
1
O snippet do @Cypher Code foi baseado no código do artigo vinculado. Bem-vindo a ler a pergunta na íntegra. Oh. Esperar.
Stephen Stephen
1
@ Macher eu quis dizer isso como herança clássica. Má escolha de redação da minha parte.
Stephen
9

Isso tem uma enorme armadilha que se você escrevesse

Student.prototype.constructor = Student;

mas se houvesse um professor cujo protótipo também fosse Person e você escreveu

Teacher.prototype.constructor = Teacher;

então o construtor Student é agora professor!

Editar: Você pode evitar isso, garantindo que definiu os protótipos de Aluno e Professor usando novas instâncias da classe Person criada usando Object.create, como no exemplo do Mozilla.

Student.prototype = Object.create(Person.prototype);
Teacher.prototype = Object.create(Person.prototype);
James D
fonte
1
Student.prototype = Object.create(...)é assumido nesta questão. Essa resposta não acrescenta nada além de uma possível confusão.
André Chalella 6/11/2015
3
@ AndréNeves Achei esta resposta útil. Object.create(...)é usado no artigo MDN que gerou a pergunta, mas não na pergunta em si. Tenho certeza que muitas pessoas não clicam.
21880 Alex Ross
O artigo vinculado mencionado na pergunta já usa Object.create (). Esta resposta ea Editar ot a resposta não são realmente relevantes, e que é confuso para dizer o mínimo :-)
Drenai
1
O ponto mais amplo é que existem truques que atrapalharão as pessoas novas nos protótipos de Javascript. Se estamos discutindo em 2016, você realmente deve usar as classes ES6, Babel e / ou TypeScript. Mas se você realmente deseja construir classes manualmente dessa maneira, ajuda a entender como as cadeias de protótipos realmente funcionam para alavancar seu poder. Você pode usar qualquer objeto como um protótipo e talvez não queira criar um novo objeto em separado. Além disso, antes que o HTML 5 fosse totalmente difundido, o Object.create nem sempre estava disponível, portanto era mais fácil configurar uma classe incorretamente.
James D
5

Até agora, a confusão ainda está lá.

Seguindo o exemplo original, como você possui um objeto existente student1como:

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

Suponha que você não queira saber como student1é criado, apenas deseja outro objeto como esse, você pode usar a propriedade construtora de student1like:

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

Aqui, ele falhará ao obter as propriedades, Studentse a propriedade do construtor não estiver configurada. Em vez disso, ele criará um Personobjeto.

Mahavir
fonte
2

Temos um bom exemplo de código de por que é realmente necessário definir o construtor do protótipo.

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 ???? 
*/
user3877965
fonte
Seu createNewCarmétodo é criar fábricas !? Além disso, parece que deveria ter sido usado, em var audiFactory = new CarFactory("Audi")vez de usar herança.
21715 Bergi
Seu exemplo está usando this.constructorinternamente, portanto, não é de surpreender que tenha que ser definido. Você tem algum exemplo sem ele?
Dmitri Zaitsev
1

Não há necessidade de 'classes' da função adoçada ou usar 'Novo' hoje em dia. Use literais de objeto.

O protótipo de objeto já é uma 'classe'. Quando você define um literal de objeto, ele já é uma instância do protótipo de objeto. Eles também podem atuar como protótipo de outro 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 a pena ler :

Linguagens orientadas a objetos baseadas em classe, como Java e C ++, são baseadas no conceito de duas entidades distintas: classes e instâncias.

...

Uma linguagem baseada em protótipo, como JavaScript, não faz essa distinção: simplesmente possui objetos. Uma linguagem baseada em protótipo tem a noção de um objeto prototípico, um objeto usado como modelo para obter as propriedades iniciais de um novo objeto. Qualquer objeto pode especificar suas próprias propriedades, quando você o cria ou no tempo de execução. Além disso, qualquer objeto pode ser associado como o protótipo para outro objeto, permitindo que o segundo objeto compartilhe as propriedades do primeiro objeto

ucsarge
fonte
1

É necessário quando você precisa de uma alternativa para, toStringsem 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
fonte
O que isso deve fazer? foo.constructor()??
Ry-
0

EDIT, eu estava realmente errado. Comentar a linha de saída não muda seu comportamento. (Eu testei)


Sim, é necessário. Quando você faz

Student.prototype = new Person();  

Student.prototype.constructortorna-se Person. Portanto, a chamada Student()retornaria um objeto criado por Person. Se você faz

Student.prototype.constructor = Student; 

Student.prototype.constructoré redefinido para Student. Agora, quando você Student()o executa Student, que chama o construtor pai Parent(), ele retorna o objeto herdado corretamente. Se você não redefinir Student.prototype.constructorantes de chamá-lo, obterá um objeto que não possui nenhuma das propriedades definidas Student().

bob invisível
fonte
3
A estrutura do protótipo pode se tornar uma pessoa, mas isso é apropriado, pois está herdando todas as propriedades e métodos da Pessoa. Criar um novo Student () sem definir o prototype.constructor chama apropriadamente seu próprio construtor.
Stephen
0

Dada a função construtora simples:

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 padrão (da especificação https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/constructor ), todos os protótipos obtêm automaticamente uma propriedade chamada construtor que aponta de volta para a função qual é uma propriedade. Dependendo do construtor, outras propriedades e métodos podem ser adicionados ao protótipo, o que não é uma prática muito comum, mas ainda é permitido para extensões.

Então, basta responder: precisamos ter certeza de que o valor em prototype.constructor esteja definido corretamente, como supõe a especificação.

Sempre precisamos definir corretamente esse valor? Ajuda na depuração e torna a estrutura interna consistente com as especificações. Definitivamente, devemos quando nossa API estiver sendo usada por terceiros, mas não quando o código for finalmente executado no tempo de execução.

kospiotr
fonte
0

Aqui está um exemplo do MDN que achei muito útil para entender seus usos.

Em JavaScript, temos o async functionsque retorna o objeto AsyncFunction . AsyncFunctionnão é um objeto global, mas é possível recuperá-lo usando a constructorpropriedade e utilizá-lo.

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
fonte
-1

Não é necessário. É apenas uma das muitas coisas tradicionais que os campeões de OOP fazem para tentar transformar a herança prototípica do JavaScript em herança clássica. A única coisa que o seguinte

Student.prototype.constructor = Student; 

é que agora você tem uma referência do "construtor" atual.

Na resposta de Wayne, que foi marcada como correta, você pode fazer exatamente o mesmo que o código a seguir

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

com o código abaixo (substitua this.constructor por Person)

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

Graças a Deus que, com o ES6, os puristas de herança clássica podem usar os operadores nativos da linguagem como classe, extends e super e não precisamos ver protótipo. Correções de construtores e referências aos pais.

Roumelis George
fonte