Consider the following code:
class X {
pop() { return 'ORIGINAL'; }
}
const x = new X();
x.pop(); // 'ORIGINAL' via prototype lookup
x.pop = () => 'NEW'; // install a new pop()
x.pop(); // 'NEW' calls x's pop()
Object.getOwnPropertyDescriptors(x); // has NEW pop
Object.getOwnPropertyDescriptors(Object.getPrototypeOf(x)); // has ORIGINAL pop
When calling pop
, prototype lookup finds the ORIGINAL pop
. Why does assignment not overwrite that one instead of installing NEW pop
on x
?
It works if I do X.prototype.pop = () => 'NEW';
explicitly.
When you assign a method to an object, the method is put on the precise object that you specified in the assignment. So, when you do:
x.pop = () => 'NEW';
You're assigning a property to the object instance x
whose value is a function. The prototype of that object is not affected at all by this assignment.
When you execute a method on an object, the interpreter has a lookup sequence to find that method. First, it looks directly on the object to see if that property exists. If it finds it, that is what is executed.
So, when you then do:
x.pop()
After the previous assignment, the interpreter finds the method that assigned to the actual object and executes that one.
Before you did that method assignment, when you did x.pop()
, no method/property by that name was found on the actual object instance so the interpreter, then looks to see if there is a prototype and, if there is, it then searches the prototype chain. That's where it found the .pop()
from the class
definition and executed that one.
These are separate definitions, stored in separate places and changing one does not change the other definition. Though assigning a definition for a specific property to the object instance itself tends to "hide" the one on the prototype just because the only way you can get access to the one on the prototype after that assignment is by directly referencing the prototype because any reference to that property through an object instance itself will just find the one on the object instance itself first (thus making it so the one on the prototype is not automatically found any more).