JavaScript Object.create - наследование вложенных свойств - PullRequest
12 голосов
/ 07 июля 2010

Я столкнулся с особенностью метода Дугласа Крокфорда Object.create, который, я надеюсь, кто-то сможет объяснить:

Если я создаю объект - скажем, «человек» - используя буквенное обозначение объектазатем используйте 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'.

Буду очень признателен, если кто-нибудь сможет объяснить, почему это происходит!?

Ответы [ 3 ]

19 голосов
/ 07 июля 2010

Это происходит потому, что 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'
};
2 голосов
/ 07 июля 2010

Проблема в том, что Object.create делает только поверхностную, а не глубокую копию, поэтому person.name и anotherPerson.name оба указывают на один и тот же экземпляр объекта.

Отредактировано

Хотя это правда, что person.name === anotherPerson.name, мое объяснение, почему это правда, неверно.См. Ответ @ CMS для правильного объяснения.

1 голос
/ 07 июля 2010

Причина, по которой атрибут name не копируется, состоит в том, что литералы объектов в JavaScript всегда являются ссылками, поэтому ссылка копируется (а не ее содержимое) ... так что это не потому, что она находится глубже в цепочке прототипов или потому что он делает мелкую копию.

...