为什么两只仓鼠都吃饱了?
重要性: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,都应该写入该对象。这可以防止此类问题。