回到课程
本资料仅提供以下语言版本:English, 日本語。请 帮助我们 将其翻译为 简体中文 版本。

为什么两只仓鼠都饱了?

重要程度: 5

我们有两只仓鼠:speedylazy 都继承自普通的 hamster 对象。

当我们喂一只的同时,另一只也吃饱了。为什么?如何修复这件事?

let hamster = {
  stomach: [],

  eat(food) {
    this.stomach.push(food);
  }
};

let speedy = {
  __proto__: hamster
};

let lazy = {
  __proto__: hamster
};

// This one found the food
speedy.eat("apple");
alert( speedy.stomach ); // apple

// This one also has it, why? fix please.
alert( lazy.stomach ); // apple

我们仔细研究一下在调用 speedy.eat("apple") 的时候,发生了什么。

  1. speedy.eat 方法在原型(=hamster)中被发现,然后执行 this=speedy(在点之前的对象)。

  2. this.stomach.push() 需要查找到 stomach 属性,然后调用 push 来处理。它在 this (=speedy) 中查找 stomach,但并没有找到。

  3. 然后它顺着原型链,在 hamster 中找到 stomach

  4. 然后它调用 push ,将食物添加到胃的原型链中。

因此所有的仓鼠都有共享一个胃!

每次 stomach 从原型中获取,然后 stomach.push 修改它的“位置”。

请注意,这种情况在 this.stomach= 进行简单的赋值情况下不会发生:

let hamster = {
  stomach: [],

  eat(food) {
    // assign to this.stomach instead of this.stomach.push
    this.stomach = [food];
  }
};

let speedy = {
   __proto__: hamster
};

let lazy = {
  __proto__: hamster
};

// Speedy one found the food
speedy.eat("apple");
alert( speedy.stomach ); // apple

// Lazy one's stomach is empty
alert( lazy.stomach ); // <nothing>

现在,所有的都在正常运行,因为 this.stomach= 不会在 stomach 中执行查找。该值会被直接写入 this 对象。

此外,我们还可以通过确保每只仓鼠都有自己的胃来完全回避这个问题:

let hamster = {
  stomach: [],

  eat(food) {
    this.stomach.push(food);
  }
};

let speedy = {
  __proto__: hamster,
  stomach: []
};

let lazy = {
  __proto__: hamster,
  stomach: []
};

// Speedy one found the food
speedy.eat("apple");
alert( speedy.stomach ); // apple

// Lazy one's stomach is empty
alert( lazy.stomach ); // <nothing>

作为一种常见的解决方案,描述特定对象状态的所有属性,如上述的 stomach,通常都被写入到该对象中。这防止了类似问题的出现。