Por que o uso de construtores é desencorajado ao criar protótipos?

10

Breve histórico: No JavaScript, a função construtora para cada tipo de objeto possui uma prototypepropriedade. A prototyperefere-se a um objecto que cada uma construída usos objeto como o próximo passo na sua cadeia de protótipos. Quando você deseja que um tipo seja inerente a outro, você pode definir o prototypetipo filho como uma nova instância do tipo pai.

Por exemplo:

var Parent = function() { /* constructor business */ }
Parent.prototype.parentProp = "some parent property";

var Child = function() { /* constructor business */ }
Child.prototype = /*** !! Some prototype object goes here !! ***/

Minha pergunta pergunta sobre qual código deve estar no " Some prototype object goes here" lugar no código acima. Meu primeiro instinto é construir uma instância do pai (isto é, new Parent()), mas em um comentário a uma resposta sobre Esta é uma maneira segura de copiar um objeto de protótipo para outro? , um usuário escreve:

Não, não use new bar()para o objeto protótipo!

(... que é uma opinião que já vi em muitas respostas e comentários do SO, mas este é o único exemplo que tenho em mãos no momento.)

A outra opção é usar Object.create(Parent.prototype)como Child.prototype. Até onde eu sei, isso também cria uma nova Parentinstância, mas não executa o Parentconstrutor.

Alguém pode explicar por que evitar a execução da função construtora ao gerar um objeto de protótipo a partir de um tipo pai? Existe algum problema técnico significativo (talvez com vários níveis de herança)? Ou esse padrão é um uso indevido de construtores que entra em conflito com algumas práticas recomendadas prototípicas (por exemplo, executar o construtor ao criar um protótipo viola alguma separação de preocupações)?

apsillers
fonte

Respostas:

5

Porque é uma função que não precisa ser chamada. newnão é tão diferente de uma chamada de função regular em Javascript.

Um construtor pode fazer mais do que simplesmente definir campos. Por exemplo, se validar os dados recebidos, você causará um erro de validação quando estiver simplesmente tentando definir a cadeia de herança.

E você não precisa Object.create, isso é suficiente:

function objectCreate( proto ) {
    function T(){}
    T.prototype = proto;
    return new T();
}
Esailija
fonte
Mas é exatamente assim que Object.createé implementado.
Casey Chu
@CaseyChu de jeito nenhum. E mencionei isso porque no post vinculado alguém disse " Object.createnão funciona no IE8" - que é apenas um comentário inútil quando você pode implementá-lo para esse caso de uso em 2 segundos em qualquer navegador.
Esailija 15/05
2

Agora que meu entendimento aumentou um pouco, eu gostaria de aproveitar a resposta de Esailija com um exemplo específico:

Uma preocupação específica é que um construtor possa definir propriedades específicas da instância. Portanto, se você usar um objeto de protótipo criado com new, todas as suas instâncias filhas poderão compartilhar uma única propriedade específica da instância definida pelo construtor a partir do protótipo.

Por exemplo, suponha que as Parentinstâncias tenham uma idpropriedade única definida como tempo de construção:

var Parent = function() { this.id = Math.random(); }
Parent.prototype.parentProp = "some parent property";

var Child = function() { /* constructor business */ }
Child.prototype = new Parent();

Isso fará com que todas as Child instâncias compartilhem uma única idpropriedade de protótipo herdada do primeiro e único new Parent()objeto usado como Child.prototype.

Uma abordagem melhor para esse caso é usar Object.createe chamar diretamente o construtor pai (se necessário) dentro do construtor filho:

var Parent = function() { this.id = Math.random(); }
Parent.prototype.parentProp = "some parent property";

var Child = function() {
    // run `this` through the Parent constructor function
    Parent.apply(this, arguments);
}
Child.prototype = Object.create(Parent.prototype);
apsillers
fonte