Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why are properties of an instance updated when the prototype is updated if the property has not been set by the instance itself?

Why are properties of an instance that store primitive values, not references, updated when the prototype is updated if the property has not been set by the instance itself?

Let me give an example:

var Obj = function () {};
Obj.prototype.num = 1;

var myObj = new Obj();
var myOtherObj = new Obj();
console.log(myObj.num); //logs 1
console.log(myOtherObj.num); //logs 1

//After instances are created they still share the value (which is strange):
Obj.prototype.num = 3;
console.log(myObj.num); //logs 3
console.log(myOtherObj.num); //logs 3

//Update one of the instances property
myObj.num += 2;
console.log(myObj.num); //logs 5
console.log(myOtherObj.num); //logs 3

//Here it gets weird:
Obj.prototype.num = 4;
console.log(myObj.num); //logs 5 not updated
console.log(myOtherObj.num); //logs 4 updated

Couple of weird things here:

After an instance is created, updating the class definition updates the instance value if and only if it's never been updated by the instance itself.

If I attempt to reason it out, it seems like the initially the instance does not have its own num property and a lookup happens where it's discovered on the prototype, but once you actually set the property (internally with this.num or externally with instanceName.num) you create a property on the instance.

That doesn't seem to jive with what the ECMA5 specification says:

The value of the prototype property is used to initialise the [[Prototype]] internal property of a newly created object before the Function object is invoked as a constructor for that newly created object.

Sounds like it's supposed to populate the instances internal [[Prototype]], but yet it's still able to be modified by updating the constructor's prototype property. I get that if I set the class definition's property to an object like an array that the reference is actually getting passed to the instance and updating the array anywhere will modify the property for everyone, but these are primitive values and it doesn't seem to make much sense.

like image 968
Bjorn Avatar asked Jan 24 '26 06:01

Bjorn


2 Answers

The way this works is that properties are looked up on the object first, then if they're not found, by looking them up in the prototype.

If I attempt to reason it out, it seems like the initially the instance does not have its own num property and a lookup happens where it's discovered on the prototype, but once you actually set the property (internally with this.num or externally with instanceName.num) you create a property on the instance.property on the instance.

This is exactly how it works. If you want to remove the property that's set on an object, you can use delete:

delete myOtherObj.num;

this will remove myOtherObj's num property and allow the prototype version to show through.

The portion of the spec that you quoted is attempting to explain how the internal [[Prototype]] property is set for newly-created instances. Other than its role in property lookup, [[Prototype]] is like any other Javascript object reference.

So, when the "prototype" property of the constructor is "copied" into [[Prototype]] for a newly-created object, it actually gets a reference to the original prototype property on the constructor, which can be modified later.


As another example:

function Obj() {};
Obj.prototype.num=1;

var myObj = new Obj();
console.log(myObj.num); // logs 1

Obj.prototype = {num:3}; // replaces Obj.prototype
console.log(myObj.num); // logs 1

var myOtherObj = new Obj(); 
console.log(myOtherObj.num); // logs 3
console.log(myObj.num); // still logs 1

Note that once the object has been constructed, it keeps a reference to whatever object was set as the constructor's prototype at the time the constructor was called. Changing a property on that object will still affect any other references to the same prototype.

like image 193
Mark Bessey Avatar answered Jan 26 '26 18:01

Mark Bessey


This is completely normal, not weird. ;)

When you try to access a property of an object, that's not set in that object, the JS engine gets up the prototype chain and tries to read it there. This is true as long as the property is not explicitly set in that object.

When you change a prototype of a class the object has inherited, the property on the object itself doesn't change, it is just still not set and the property of the class is still read.

Only when you set a certain property of an object, the property is defined within that object. This is also why a certain property of an already declared object changes, when you change an object's prototype.

To clarify:

Obj.prototype.num = 3;
console.log(myObj.num); //logs 3
console.log(myOtherObj.num); //logs 3

Here you actually still read Obj.prototype.num.

myObj.num += 2;

Here you actually read Obj.prototype.num and write to myObj.num.

Obj.prototype.num = 4;
console.log(myObj.num); //logs 5 not updated
console.log(myOtherObj.num); //logs 4 updated

Now you read myObj.num and Obj.prototype.num, respectively.

like image 27
Marcel Korpel Avatar answered Jan 26 '26 20:01

Marcel Korpel



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!