Herança JavaScript: Object.create vs new


123

Em JavaScript, qual é a diferença entre estes dois exemplos:

Pré-requisito:

function SomeBaseClass(){
}

SomeBaseClass.prototype = {
    doThis : function(){
    },

    doThat : function(){
    }
}

Exemplo de herança A usando Object.create:

function MyClass(){
}

MyClass.prototype = Object.create(SomeBaseClass.prototype);

Exemplo de herança B usando a nova palavra-chave

function MyClass(){
}

MyClass.prototype = new SomeBaseClass();

Ambos os exemplos parecem fazer a mesma coisa. Quando você escolheria um sobre o outro?

Uma pergunta adicional: considere o código no link abaixo (linha 15), onde uma referência ao construtor da própria função é armazenada no protótipo. Por que isso é útil?

https://github.com/mrdoob/three.js/blob/master/src/loaders/ImageLoader.js

Trecho (se você não deseja abrir o link):

THREE.ImageLoader.prototype = {

    constructor: THREE.ImageLoader
}

23
Por que diabos isso está marcado como duplicado!?! A outra pergunta e respostas nem sequer mencionam Object.create. Isso é um erro e deve ser reaberto.
Scott Rippey

2
Se alguma coisa, é uma duplicata de stackoverflow.com/questions/4166616/...
Scott Rippey

1
13 votos nesse comentário e ainda não foi reaberto ..?!
TJ

Respostas:


111

Na sua pergunta, você mencionou que Both examples seem to do the same thingisso não é verdade, porque

Seu primeiro exemplo

function SomeBaseClass(){...}
SomeBaseClass.prototype = {
    doThis : function(){...},
    doThat : function(){...}
}
function MyClass(){...}
MyClass.prototype = Object.create(SomeBaseClass.prototype);

Neste exemplo, você está apenas herdando, SomeBaseClass' prototypemas e se você tiver uma propriedade SomeBaseClasscomo você

function SomeBaseClass(){ 
    this.publicProperty='SomeValue'; 
}

e se você usar como

var obj=new MyClass();
console.log(obj.publicProperty); // undefined
console.log(obj);​

O objobjeto não terá publicPropertypropriedades como neste exemplo .

Seu segundo exemplo

MyClass.prototype = new SomeBaseClass();

Ele está executando a constructorfunção, criando uma instância SomeBaseClasse herdando o SomeBaseClassobjeto inteiro . Então, se você usar

    var obj=new MyClass();
    console.log(obj.publicProperty); // SomeValue
    console.log(obj);​

Nesse caso, sua publicPropertypropriedade também está disponível para o objobjeto, como neste exemplo .

Como o Object.createnão está disponível em alguns navegadores antigos, nesse caso, você pode usar

if(!Object.create)
{
    Object.create=function(o){
        function F(){}
        F.prototype=o;
        return new F();
    }
}

O código acima apenas adiciona Object.createfunção se não estiver disponível para que você possa usar a Object.createfunção e acho que o código acima descreve o que Object.createrealmente faz. Espero que ajude de alguma forma.


6
Oi Sheikh. Obrigado por seus esforços nisso. Sim, a diferença é que o construtor é executado no segundo exemplo, mas não no primeiro. (o que é desejável no meu caso). Em relação à propriedade pública indefinida que não é herdada da super implementação, você só precisa chamar super no construtor da criança: SomeBaseClass.call (this). Verifique este violino: jsfiddle.net/NhQGB
ChrisRich

Eu estive procurando uma maneira super simples de herança adequada em JS sem usar bibliotecas / estruturas. Penso que este exemplo (no meu violão acima) é a melhor abordagem para navegadores modernos. Talvez o polyfill Object.Create possa adicionar suporte para navegadores herdados?
31412 ChrisRich

Então, basicamente, para resumir, se você fizer .prototype = new, estará herdando todos os valores atribuídos a ele na classe base e, quando fizer o objeto.criar, estará apenas herdando o que está no protótipo na classe base, certo?
Davidjnelson

Isso "MyClass.prototype = new SomeBaseClass ()" significa que a nova instância de SomeBaseClass é criada quando a instância de MyClass ainda não foi criada?

Não, somente quando você cria o objeto principal, o protótipo é definido para isso SomeBaseClass.
The Alpha

39

Ambos os exemplos parecem fazer a mesma coisa.

Isso é verdade no seu caso.

Quando você escolheria um sobre o outro?

Quando SomeBaseClasstem um corpo de função, isso seria executado com a newpalavra - chave Isso geralmente não se destina - você só deseja configurar a cadeia de protótipos. Em alguns casos, pode até causar problemas sérios porque você realmente instancia um objeto, cujas variáveis ​​de escopo privado são compartilhadas por todas as MyClassinstâncias, pois herdam os mesmos métodos privilegiados. Outros efeitos colaterais são imagináveis.

Então, você geralmente deve preferir Object.create. No entanto, ele não é suportado em alguns navegadores herdados; é por isso que você vê a newabordagem com muita frequência, pois muitas vezes não causa danos (óbvios). Também dê uma olhada nesta resposta .


Esta é a melhor resposta, bem explicado
Spex

8

A diferença se torna óbvia se você usar Object.create()como pretendido. Na verdade, oculta completamente a prototypepalavra do seu código, ele faz o trabalho sob o capô. Usando Object.create(), podemos ir como

var base =  {
    doThis : function(){
    },

    doThat : function(){
    }
};

E então podemos estender / herdar outros objetos disso

var myObject = Object.create( base );
// myObject will now link to "base" via the prototype chain internally

Portanto, esse é outro conceito, uma maneira mais "orientada a objetos" de herdar. Não há "função construtora" pronta Object.create()para uso, por exemplo. Mas é claro que você pode apenas criar e chamar uma função de construtor autodefinida dentro desses objetos.

Um argumento para usar Object.create()é que ele pode olhar mais naturais para misturar / * herdar * de outros objetos, que usando Javascript maneira padrão .


7
Object.createrealmente não pode substituir a abordagem clássica com newquando uma função de construtor é necessário
Bergi

1
Definitivamente, pode @Bergi. Veja esta postagem do blog: davidwalsh.name/javascript-objects-deconstruction . Você também pode passar um objeto de inicialização como um segundo parâmetro para Object.create ().
usar o seguinte código

@LocalPCGuy: Não, não pode, porque Object.createnão chama uma função que seria necessária para criar fechamentos. É claro que Object.createvocê pode colocar um initmétodo em seus objetos e chamá-lo imediatamente, e talvez até retorne thispara obter uma sintaxe concisa - mas espere, esse é um construtor.
Bergi 18/04/2015

1
Chamar uma função init funciona de maneira semelhante a um construtor, mas não é um construtor. E esse método permite substituir exatamente a abordagem clássica por Object.create. E o modelo mental da ligação a objetos é muito mais simples do que o criado por meio de 'novo'.
usar o seguinte código

Bem, meu modelo mental de newusa "ligação a objetos", então não há muita diferença. De fato, existem apenas duas coisas: .constructoré chamada .inite essa função não tem uma .prototypepropriedade apontando para o seu objeto protótipo. O resto é apenas sintaxe - e eu prefiro o new Xcontrário Object.create(x).init().
Bergi 18/04/2015

0

Eu não sou especialista em java script, mas aqui está um exemplo simples para entender a diferença entre "Object.create" e "new" ..

etapa 1: crie a função pai com algumas propriedades e ações.

function Person() {

this.name = 'venkat';

this.address = 'dallas';

this.mobile='xxxxxxxxxx'

}

Person.prototype.func1 = function () {

    return this.name + this.address;
}

etapa 2: crie uma função filha (PersonSalary) que se estenda acima da função Pessoa usando Nova palavra-chave.

function PersonSalary() {
    Person.call(this);
}
PersonSalary.prototype = new Person();

PersonSalary();

etapa 3: crie a segunda função filho (PersonLeaves) que se estende acima da função Person usando a palavra-chave Object.create .

function PersonLeaves() {
 Person.call(this);
}
PersonLeaves.prototype = Object.create(Person.prototype);


PersonLeaves();

// Agora verifique os protótipos das funções filho.

PersonSalary.prototype
PersonLeaves.prototype

ambas as funções filho vincularão ao protótipo Pessoa (função pai) e poderão acessar seus métodos, mas se você criar uma função filho usando new, retornará um novo objeto com todas as propriedades pai de que não precisamos e também quando você criar qualquer objeto ou função usando "Novo" que a função pai é executada e que não queremos que seja.

Aqui estão os tópicos

se você apenas deseja delegar para alguns métodos na função pai e não deseja que um novo objeto seja criado, usar Object.create é a melhor maneira.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.