Чому обидва хом’ячка наситились?
Ми маємо два хом’ячка (об’єкти): speedy та lazy, які успадковують властивості від загального об’єкта hamster.
Коли ми годуємо одного з них, інший також стає ситим. Але чому? Як ми можемо це виправити?
let hamster = {
stomach: [],
eat(food) {
this.stomach.push(food);
}
};
let speedy = {
__proto__: hamster
};
let lazy = {
__proto__: hamster
};
// Цей хом’ячок знайшов їжу
speedy.eat("apple");
alert( speedy.stomach ); // apple
// Але цей також має їжу, чому? Виправте це.
alert( lazy.stomach ); // apple
Подивімося уважно, що відбувається у виклику speedy.eat("apple").
-
Метод
speedy.eatзнаходиться в прототипі (=hamster), і виконується зthis=speedy(об’єкт перед крапкою). -
Потім
this.stomach.push()повинен знайти властивістьstomachі викликатиpushна ньому. Він шукаєstomachвthis(=speedy), але нічого не знаходить. -
Далі
stomachйде по ланцюжку прототипів доhamster. -
Потім він викликає
pushна ньому, додаючи їжу до шлунку прототипу.
Таким чином, усі хом’ячки мають спільний шлунок!
Для обох методів lazy.stomach.push(...) і speedy.stomach.push(), властивість stomach знаходисться в прототипі (бо в самих об’єктах такої властивості немає), яка отримує нові дані.
Зауважте, що така річ не відбувається у випадку простого визначення this.stomach=:
let hamster = {
stomach: [],
eat(food) {
// визначається до `this.stomach` замість `this.stomach.push`
this.stomach = [food];
}
};
let speedy = {
__proto__: hamster
};
let lazy = {
__proto__: hamster
};
// Хом’ячок 'Speedy' знайшов їжу
speedy.eat("apple");
alert( speedy.stomach ); // apple
// Шлунок хом’ячка 'Lazy' пустий
alert( lazy.stomach ); // <нічого>
Тепер все працює добре, тому що this.stomach= не виконує пошук властивості stomach. Значення записується прямо в this об’єкта.
Також, ми можемо узагалі уникнути проблеми визначивши шлунок для кожного хом’ячка окремо, ось так:
let hamster = {
stomach: [],
eat(food) {
this.stomach.push(food);
}
};
let speedy = {
__proto__: hamster,
stomach: []
};
let lazy = {
__proto__: hamster,
stomach: []
};
// Хом’ячок `Speedy` знайшов їжу
speedy.eat("яблуко");
alert( speedy.stomach ); // яблуко
// Шлунок хом’ячка `Lazy` пустий
alert( lazy.stomach ); // <нічого>
Отже, спільним рішенням може бути те, що всі властивості, які описують стан конкретного об’єкта (подібно як stomach), повинні бути записані (визначені) в цьому ж самому об’єкті. Це уникне подібної проблеми.