JavaScript Object.create -- inheriting nested properties
I've come across a peculiarity with Douglas Crockfords Object.create method which I'm hoping someone might be able to explain:
If I create an object - say 'person' - using object literal notation then use Object.create to create a new object - say 'anotherPerson' - which inherits the methods and properties from the initial 'person' object.
If I then change the name values of the second object - 'anotherPerson' - it also changes the name value of the initial 'person' object.
This only happens when the properties are nested, this code should give you an idea of what I mean:
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'
If I were to store the name values without nesting then this odd behaviour does not occur -- e.g.
// initiate new 'person' object
var person = {
firstName: 'Ricky',
lastName: 'Gervais',
talk开发者_如何学Go: 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'
This nesting issue doesn't seem to occur when using a classical style of inheritance with a constructor function and the 'new' keyword.
I'd be much appreciative if anyone's able to explain why this occurs!?
That happens because anotherPerson.name
is an object and it is stored upper in the prototype chain, on the original person
object:
//...
var anotherPerson = Object.create(person);
anotherPerson.hasOwnProperty('name'); // false, the name is inherited
person.name === anotherPerson.name; // true, the same object reference
You can avoid this by assigning a new object to the name
property of the newly created object:
// create anotherPerson from person
var anotherPerson = Object.create(person);
anotherPerson.name = {
first: 'Stephen',
last: 'Merchant'
};
The problem is that Object.create only does a shallow copy, not a deep copy, so person.name and anotherPerson.name both point to the same Object instance.
Edited
While it's true that person.name === anotherPerson.name
, my explanation for why this is true is incorrect. See @CMS's answer for the correct explanation.
The reason the name
attribute isn't copied is because object literals in JavaScript are always references, therefore the reference is copied (not its content) ... so it is not because it is deeper in the prototype chain or because it's doing a shallow copy.
精彩评论