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?
javascript
oop
inheritance
trinto
fonte
fonte
subclass.prototype.constructor
apontará paraparent_class
se você não escreversubclass.prototype.constructor = subclass
; Ou seja, o usosubclass.prototype.constructor()
direto produzirá um resultado inesperado.Respostas:
Nem sempre é necessário, mas tem seus usos. Suponha que desejássemos fazer um método de cópia na
Person
classe base . Como isso:Agora, o que acontece quando criamos um novo
Student
e o copiamos?A cópia não é uma instância de
Student
. Isso ocorre porque (sem verificações explícitas), não teríamos como retornar umaStudent
cópia da classe "base". Nós podemos somente retornar aPerson
. No entanto, se tivéssemos reiniciado o construtor:... então tudo funciona como esperado:
fonte
constructor
atributo não tem nenhum significado especial em JS, portanto, é possível chamá-lobananashake
. A única diferença é que o motor automaticamente inicializaconstructor
emf.prototype
quando você declara uma funçãof
. No entanto, pode ser substituído a qualquer momento.constructor
significa que ele faz tem significado especial em JS, praticamente por definição.return new this.constructor(this.name);
vez dereturn new Person(this.name);
. Comothis.constructor
é aStudent
função (porque você a defineStudent.prototype.constructor = Student;
), acopy
função acaba chamando aStudent
função. Não tenho certeza de qual era sua intenção com o//just as bad
comentário.Student
construtor tivesse adicionado um argumento adicional comoStudent(name, id)
:? Precisamos substituir acopy
função, chamando aPerson
versão de dentro dela e também copiando aid
propriedade adicional ?Sim e não.
No ES5 e versões anteriores, o próprio JavaScript não usava
constructor
para nada. Ele definiu que o objeto padrão naprototype
propriedade 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#then
usa aconstructor
propriedade 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á lá por padrão, você só precisa colocá-lo novamente quando substituir o objeto na
prototype
propriedade de uma função :Se você não fizer isso:
... então
Student.prototype.constructor
herda daPerson.prototype
qual (presumivelmente) temconstructor = Person
. Então é enganoso. E, claro, se você estiver subclassificando algo que o usa (comoPromise
ouArray
) e não usandoclass
¹ (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
class
palavra-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¹ "... se você subclasse algo que o utiliza (como
Promise
ouArray
) e não usandoclass
..." - É possível fazer isso, mas é uma dor real (e um pouco bobo). Você tem que usarReflect.construct
.fonte
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
prototype
objeto. A substituição de umprototype
objeto 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:
Quando você chamar o construtor para instanciar, diga
Adam
:A
new
palavra-chave invocada com 'Person' basicamente executará o construtor Person com algumas linhas de código adicionais:Se nós
console.log(adam.species)
, a pesquisa falharmos naadam
instância, e procurar a cadeia de protótipos na sua.prototype
, que éPerson.prototype
- ePerson.prototype
tem uma.species
propriedade, para que a pesquisa seja bem-sucedidaPerson.prototype
. Em seguida, ele será registrado'human'
.Aqui, o
Person.prototype.constructor
apontará corretamente paraPerson
.Então agora a parte interessante, a chamada 'subclasse'. Se queremos criar uma
Student
turma, que é uma subclasse daPerson
turma com algumas alterações adicionais, precisamos garantir que osStudent.prototype.constructor
pontos apontem para precisão do aluno.Não faz isso por si só. Quando você subclasse, o código fica assim:
Ligar
new Student()
aqui retornaria um objeto com todas as propriedades que queremos. Aqui, se checarmoseve instanceof Person
, ele retornariafalse
. Se tentarmos acessareve.species
, ele retornaráundefined
.Em outras palavras, precisamos conectar a delegação para que
eve instanceof Person
retorne true e para que instâncias deStudent
delegar corretamente paraStudent.prototype
e depoisPerson.prototype
.MAS, como a chamamos com a
new
palavra - chave, lembre-se do que essa invocação adiciona? Isso chamariaObject.create(Student.prototype)
, e é assim que estabelecemos esse relacionamento delegacional entreStudent
eStudent.prototype
. Observe que, no momento,Student.prototype
está vazio. Portanto, procurar.species
uma instância deStudent
falharia, pois delega apenasStudent.prototype
, e a.species
propriedade não existeStudent.prototype
.Quando atribuímos
Student.prototype
aObject.create(Person.prototype)
,Student.prototype
ele próprio delega ePerson.prototype
, olhando para cimaeve.species
, retornaráhuman
como esperamos. Presumivelmente, gostaríamos que herdasse do Student.prototype AND Person.prototype. Então, precisamos consertar tudo isso.Agora a delegação funciona, mas estamos substituindo
Student.prototype
por um dePerson.prototype
. Então, se ligarmosStudent.prototype.constructor
, apontaria para emPerson
vez deStudent
. Este é por isso que precisamos corrigi-lo.No ES5, nossa
constructor
propriedade é uma referência que se refere a uma função que escrevemos com a intenção de ser um 'construtor'. Além do que anew
palavra - chave nos fornece, o construtor é uma função "simples".No ES6, o
constructor
agora 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 umsuper
quando estamos estendendo uma classe existente. Então, escreveríamos o código acima dessa maneira:fonte
eve instanceof Student
retornadotrue
. Consulte stackoverflow.com/questions/35537995/… para obter explicações. Além disso, quando você diz awhich is, at the moment, nothing
que está se referindo? Toda função tem um protótipo, portanto, se eu verificarStudent.prototype
, é algo.Object.create(Person.prototype)
, oStudent.prototype
está vazio. Portanto, se registrarmoseve.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.which is, at the moment, nothing
eu quis dizer que oStudent.prototype
objeto está vazio.Student.prototype
paraObject.create(Person.prototype)
- que é, se você se lembrar, da mesma maneira que todas as instâncias de Person são configuradas para delegarPerson.prototype
-, procurar uma propriedade em uma instância deStudent
delegaria apenasStudent.prototype
. Entãoeve.species
, falhará na pesquisa. Se o atribuirmos, eleStudent.prototype
próprio delega paraPerson.prototype
, e olhando para cimaeve.species
retornaráhuman
.instance
o Construtor '' subclasse '', será preciso." Não,instanceof
não usaconstructor
. "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?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:
e no final do código de teste ...
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.
fonte
Isso tem uma enorme armadilha que se você escrevesse
mas se houvesse um professor cujo protótipo também fosse Person e você escreveu
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.
fonte
Student.prototype = Object.create(...)
é assumido nesta questão. Essa resposta não acrescenta nada além de uma possível confusão.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.Até agora, a confusão ainda está lá.
Seguindo o exemplo original, como você possui um objeto existente
student1
como:Suponha que você não queira saber como
student1
é criado, apenas deseja outro objeto como esse, você pode usar a propriedade construtora destudent1
like:Aqui, ele falhará ao obter as propriedades,
Student
se a propriedade do construtor não estiver configurada. Em vez disso, ele criará umPerson
objeto.fonte
Temos um bom exemplo de código de por que é realmente necessário definir o construtor do protótipo.
fonte
createNewCar
método é criar fábricas !? Além disso, parece que deveria ter sido usado, emvar audiFactory = new CarFactory("Audi")
vez de usar herança.this.constructor
internamente, portanto, não é de surpreender que tenha que ser definido. Você tem algum exemplo sem ele?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.
Vale a pena ler :
fonte
É necessário quando você precisa de uma alternativa para,
toString
sem monkeypatching:fonte
foo.constructor()
??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.constructor
torna-sePerson
. Portanto, a chamadaStudent()
retornaria um objeto criado porPerson
. Se você fazStudent.prototype.constructor
é redefinido paraStudent
. Agora, quando vocêStudent()
o executaStudent
, que chama o construtor paiParent()
, ele retorna o objeto herdado corretamente. Se você não redefinirStudent.prototype.constructor
antes de chamá-lo, obterá um objeto que não possui nenhuma das propriedades definidasStudent()
.fonte
Dada a função construtora simples:
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.
fonte
Aqui está um exemplo do MDN que achei muito útil para entender seus usos.
Em JavaScript, temos o
async functions
que retorna o objeto AsyncFunction .AsyncFunction
não é um objeto global, mas é possível recuperá-lo usando aconstructor
propriedade e utilizá-lo.fonte
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
é 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
com o código abaixo (substitua this.constructor por Person)
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.
fonte