13

我遇到了 Douglas Crockfords Object.create 方法的一个特点,我希望有人能解释一下:

如果我创建一个对象——比如“person”——使用对象文字表示法,然后使用 Object.create 创建一个新对象——比如“anotherPerson”——它继承了初始“person”对象的方法和属性。

如果我然后更改第二个对象的名称值 - 'anotherPerson' - 它也会更改初始'person' 对象的名称值。

这仅在属性嵌套时发生,这段代码应该让您了解我的意思:

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'

如果我要在不嵌套的情况下存储名称值,那么这种奇怪的行为就不会发生——例如

// 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'

当使用带有构造函数和“new”关键字的经典继承风格时,似乎不会出现这种嵌套问题。

如果有人能够解释为什么会发生这种情况,我将不胜感激!?

4

3 回答 3

20

发生这种情况是因为anotherPerson.name是一个对象,它存储在原型链的上层,在原始person对象上:

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

您可以通过将新对象分配给新创建对象的属性来避免这种name情况:

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

anotherPerson.name = {
  first: 'Stephen',
  last: 'Merchant'
};
于 2010-07-07T00:23:13.693 回答
2

问题是 Object.create 只做浅拷贝,而不做深拷贝,所以 person.name 和 anotherPerson.name 都指向同一个 Object 实例。

已编辑

虽然这是真的,person.name === anotherPerson.name但我对为什么这是真的解释是不正确的。有关正确解释,请参阅@CMS 的答案。

于 2010-07-07T00:23:02.100 回答
1

属性不被复制的原因name是因为 JavaScript 中的对象文字总是引用,因此引用被复制(不是它的内容)......所以这不是因为它在原型链中更深,或者因为它正在做一个浅拷贝.

于 2010-07-07T00:46:55.700 回答