Why are both hamsters full?
We have two hamsters: speedy
and lazy
inheriting from the general hamster
object.
When we feed one of them, the other one is also full. Why? How can we fix it?
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
Let’s look carefully at what’s going on in the call speedy.eat("apple")
.
-
The method
speedy.eat
is found in the prototype (=hamster
), then executed withthis=speedy
(the object before the dot). -
Then
this.stomach.push()
needs to findstomach
property and callpush
on it. It looks forstomach
inthis
(=speedy
), but nothing found. -
Then it follows the prototype chain and finds
stomach
inhamster
. -
Then it calls
push
on it, adding the food into the stomach of the prototype.
So all hamsters share a single stomach!
Both for lazy.stomach.push(...)
and speedy.stomach.push()
, the property stomach
is found in the prototype (as it’s not in the object itself), then the new data is pushed into it.
Please note that such thing doesn’t happen in case of a simple assignment 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>
Now all works fine, because this.stomach=
does not perform a lookup of stomach
. The value is written directly into this
object.
Also we can totally avoid the problem by making sure that each hamster has their own 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>
As a common solution, all properties that describe the state of a particular object, like stomach
above, should be written into that object. That prevents such problems.