2010-07-07 21 views
11

Me he encontrado con una peculiaridad con el método de Douglas Crockfords Object.create la que estoy esperando que alguien podría ser capaz de explicar:JavaScript Object.create - heredar propiedades anidadas

Si creo un objeto - por ejemplo ' persona '- usando la notación literal del objeto, luego use Object.create para crear un nuevo objeto, digamos' anotherPerson ', que hereda los métodos y propiedades del objeto' persona 'inicial.

Si cambio los valores del nombre del segundo objeto - 'otra persona' - también cambia el valor del nombre del objeto 'persona' inicial.

Esto sólo ocurre cuando las propiedades están anidados, este código debe darle una idea de lo que quiero decir:

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

// initiate new 'person' object 
var person = { 
    name: { 
     first: 'Ricky', 
     last: 'Gervais' 
    }, 
    talk: function() { 
     console.log('my name is ' + this.name.first + ' ' + this.name.last); 
    } 
} 

// create anotherPerson from person.prototype 
var anotherPerson = Object.create(person); 
// change name of anotherPerson 
anotherPerson.name.first = 'Stephen'; 
anotherPerson.name.last = 'Merchant'; 

// call talk method of both 'person' and 'anotherPerson' objects 
person.talk(); // oddly enough, prints 'Stephen Merchant' 
anotherPerson.talk(); // prints 'Stephen Merchant' 

Si tuviera que almacenar los valores de nombre y sin anidación entonces este extraño comportamiento no se produce - - p.ej

// initiate new 'person' object 
var person = { 
    firstName: 'Ricky', 
    lastName: 'Gervais', 
    talk: function() { 
     console.log('my name is ' + this.firstName + ' ' + this.lastName); 
    } 
} 

// create anotherPerson from person.prototype 
var anotherPerson = Object.create(person); 
// change name of anotherPerson 
anotherPerson.firstName = 'Stephen'; 
anotherPerson.lastName = 'Merchant'; 

// call talk method of both 'person' and 'anotherPerson' objects 
person.talk(); // prints 'Ricky Gervais' 
anotherPerson.talk(); // prints 'Stephen Merchant' 

Este problema anidación no parece ocurrir cuando se utiliza un estilo clásico de la herencia con una función constructora y la palabra clave 'nuevo'.

Estaría muy agradecido si alguien puede explicar por qué ocurre esto?

+2

posible duplicar: [herencia de Prototypal de Crockford - Problemas con objetos anidados] (http://stackoverflow.com/q/10131052/1048572) – Bergi

Respuesta

18

Eso sucede porque anotherPerson.name es un objeto y se almacena superior en la cadena de prototipo, en el original person objeto:

//... 
var anotherPerson = Object.create(person); 
anotherPerson.hasOwnProperty('name'); // false, the name is inherited 
person.name === anotherPerson.name; // true, the same object reference 

Esto se puede evitar mediante la asignación de un nuevo objeto a la propiedad name de la recién objeto creado:

// create anotherPerson from person 
var anotherPerson = Object.create(person); 

anotherPerson.name = { 
    first: 'Stephen', 
    last: 'Merchant' 
}; 
+0

¡Guau, el desbordamiento de la pila es más como un mensajero instantáneo que un foro! Gracias por la respuesta, tiene sentido, por lo que el segundo objeto solo tiene el nombre objeto por referencia, en realidad no se copia. Entonces asumo que el objeto 'persona' original evaluaría hasOwnProperty ('nombre') === verdadero ya que es el prototipo de 'otroPersona'. – Richard

+1

Soy nuevo en Javascript, pero supongo que también podría usar: 'anotherPerson.name = Object.create (person.name);' hacer que el nuevo objeto anidado herede del antiguo objeto anidado si lo desea. –

+0

Esto ya es una respuesta antigua pero todavía tengo una pregunta para esto. Entiendo que B.name no existe y sube para encontrar A.name, por lo que reemplaza el valor de A.name. Sin embargo, ¿por qué en el segundo ejemplo, el "B.firstName/lastName" existe en el objeto B y no anula las propiedades de A? – user2734550

2

El problema es que Object.create solo hace una copia superficial, no una copia profunda, por lo que person.name y anotherPerson.name apuntan a la misma instancia de Object.

Editado

Si bien es cierto que person.name === anotherPerson.name, mi explicación de por qué esto es cierto no es correcto. Consulte la respuesta de @CMS para la explicación correcta.

+2

Verdadero, ambos apuntan a la misma instancia, pero en realidad, 'Object.create' doesn 't hace una copia de objeto en absoluto, simplemente crea un nuevo * objeto vacío * que * hereda * del original. – CMS

+0

Gracias por la respuesta. ¿Conoces algún artículo decente sobre copias superficiales y profundas? – Richard

+0

@CMS, en realidad 'F.prototype = o;' es una copia. Copia el objeto y sus propiedades dentro del prototipo del nuevo objeto ... y la razón por la cual no se copia el atributo 'name' es porque los literales de objetos en JavaScript son siempre referencias, por lo tanto, la referencia se copia (no su contenido). .. así que no es porque sea más profundo en la cadena de prototipos o porque está haciendo una copia superficial. –

1

la razón name el atributo no se copia se debe a que los literales de objetos en JavaScript son siempre referencias, por lo tanto, se copia la referencia (no su contenido) ... así que no es porque sea más profundo en la cadena de prototipos o porque está haciendo una copia superficial.

+0

Esto tiene mucho sentido para mí ... ¿sigue siendo correcto? – ryansstack

Cuestiones relacionadas