назад до уроку

Чому обидва хом’ячка наситились?

важливість: 5

Ми маємо два хом’ячка (об’єкти): 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").

  1. Метод speedy.eat знаходиться в прототипі (=hamster), і виконується з this=speedy (об’єкт перед крапкою).

  2. Потім this.stomach.push() повинен знайти властивість stomach і викликати push на ньому. Він шукає stomach в this (=speedy), але нічого не знаходить.

  3. Далі stomach йде по ланцюжку прототипів до hamster.

  4. Потім він викликає 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), повинні бути записані (визначені) в цьому ж самому об’єкті. Це уникне подібної проблеми.