為什麼兩隻倉鼠都吃飽了?
重要性: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
,將食物加入 原型中的 stomach。
因此,所有倉鼠共用一個 stomach!
對於 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
物件。
我們也可以透過確保每個倉鼠都有自己的 stomach 來完全避免問題。
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
,都應該寫入該物件。這樣可以防止此類問題。