Reputation: 1619
I did some test in Chrome. Quite simple, I change the inherited property value. I assume the set would happen on prototype object, but the prototype value does not change.
Actually a new property is created on child object. Is this expected?
> function Shape() {};
> var base = new function() { this.mValue = 10; };
> Shape.prototype = base;
> var s = new Shape();
> s.mValue = 20;
> base;
< Object {mValue: 10}
Upvotes: 2
Views: 83
Reputation: 1074295
Set the property value inherited from prototype, why the prototype value does not change?
Because when you assign to s.mValue
, it creates a new property on s
which shadows the one on the prototype.
Property access works like this:
If you're getting the value of the property, it first looks at the object itself to see if it has it. If so, it returns that object's value for it. If not, it looks to the object's prototype and returns the prototype's value if the prototype has it; if not, it goes to the prototype's prototype, and so on.
If you're setting the value of the property, it sets it on the object itself, it doesn't go up the prototype chain.
Obviously that's the simple version. :-)
This can be a bit surprising if you're coming to it from class-based languages, but once you understand it, it's really simple.
Let's throw some ASCII-art at it for clarity. After this code:
function Shape() {};
var base = new function() { this.mValue = 10; };
Shape.prototype = base;
...we have something like this in memory:
+−−−−−−−−−−−−−−−−−−+ Shape−−>| function | +−−−−−−−−−−−−−−−−−−+ | [[Prototype]] |−−−−−> Function.prototype | prototype |−+ +−−−−−−−−−−−−−−−−−−+ | +−−−−−−−−−−−−−−−+ +−−−>| object | | +−−−−−−−−−−−−−−−+ base−−−−−−−−−−−−−−−−−−−−−−−−−+ | [[Prototype]] |−−−> Object.prototype | mValue: 10 | +−−−−−−−−−−−−−−−+
(If prototype
vs. [[Prototype]]
is unfamiliar to you, see the explanation at ¹ at the end of the answer.)
Then we do:
var s = new Shape();
and get:
+−−−−−−−−−−−−−−−−−−+ Shape−−>| function | +−−−−−−−−−−−−−−−−−−+ | [[Prototype]] |−−−−−> Function.prototype | prototype |−+ +−−−−−−−−−−−−−−−−−−+ | +−−−−−−−−−−−−−−−+ +−+−>| object | | | +−−−−−−−−−−−−−−−+ base−−−−−−−−−−−−−−−−−−−−−−−−−+ | | [[Prototype]] |−−−> Object.prototype | | mValue: 10 | +−−−−−−−−−−−−−−−−−−+ | +−−−−−−−−−−−−−−−+ s−−−−−−>| object | | +−−−−−−−−−−−−−−−−−−+ | | [[Prototype]] |−−−+ +−−−−−−−−−−−−−−−−−−+
At this point, if we did console.log(s.mValue)
, the JavaScript engine would look at s
and, not finding a property with the name mValue
, it would follow the __proto__
link to get the prototype, and look there. Finding it there, it gets the value from there.
Then we do:
s.mValue = 20;
and get:
+−−−−−−−−−−−−−−−−−−+ Shape−−>| function | +−−−−−−−−−−−−−−−−−−+ | [[Prototype]] |−−−−−> Function.prototype | prototype |−+ +−−−−−−−−−−−−−−−−−−+ | +−−−−−−−−−−−−−−−+ +−+−>| object | | | +−−−−−−−−−−−−−−−+ base−−−−−−−−−−−−−−−−−−−−−−−−−+ | | [[Prototype]] |−−−> Object.prototype | | mValue: 10 | +−−−−−−−−−−−−−−−−−−+ | +−−−−−−−−−−−−−−−+ s−−−−−−>| object | | +−−−−−−−−−−−−−−−−−−+ | | [[Prototype]] |−−−+ | mValue: 20 | +−−−−−−−−−−−−−−−−−−+
At this point, if we did console.log(s.mValue)
, the JavaScript engine would look at s
, find the property, and get its value from s
.
Side note: var base = new function() { this.mValue = 10; };
is a really long way to write var base = { mValue: 10 }
. ;-) And it very very very slightly slows down access to the properties inherited from Object.prototype
, as it inserts an extra unnecessary prototype in the chain. Also, you don't need to create a new object for Shape
to assign to instances; it already has one.
So that code can be simplified to:
function Shape() {} // No need for ; after function declarations, they're not statements
Shape.prototype.mValue = 10;
var s = new Shape();
s.mValue = 20;
Shape.prototype;
(And as of this update to the answer in 2023, we probably wouldn't use var
.)
Just FWIW.
¹ Re prototype
and [[Prototype]]
:
prototype
is a property some functions have that points to the object to assign as the prototype of an object created via new
with the function. Example: Date.prototype
points to the object that is used as the prototype of date instances created via new Date
.[[Prototype]]
is the internal field of objects that points to the object's prototype. Example: If you did const d = new Date()
, d'
s [[Prototype]]
field points to Date.prototype
. The [[Prototype]]
field isn't accessible directly in code. You can access it by using Object.getPrototypeOf(d)
, or (not recommended) by using the accessor property called __proto__
defined in Object.prototype
. (But don't use __proto__
in new code. It's only for backward-compatibility with code written for a JavaScript engine that added that feature before standardization made it unnecessary, and note that objects that don't inherit from Object.prototype
don't have it.)Upvotes: 4