为什么两只仓鼠都吃饱了?
重要性:5
我们有两个仓鼠:speedy
和 lazy
,它们继承自通用 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")
调用中发生了什么。
-
方法
speedy.eat
在原型 (=hamster
) 中找到,然后用this=speedy
(点号之前的对象) 执行。 -
然后
this.stomach.push()
需要找到stomach
属性并调用其上的push
。它在this
(=speedy
) 中查找stomach
,但没有找到。 -
然后它沿着原型链向上查找,并在
hamster
中找到stomach
。 -
然后它调用其上的
push
,将食物添加到原型的胃中。
所以所有仓鼠共用一个胃!
对于 lazy.stomach.push(...)
和 speedy.stomach.push()
,属性 stomach
在原型中找到(因为它不在对象本身中),然后将新数据推入其中。
请注意,在简单赋值 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
,都应该写入该对象。这可以防止此类问题。