Just when I thought I had JS figured out, I get hung up on this:
function Obj() {
console.log('x: %s, o.x: %s', this.x++, this.o.x++);
}
Obj.prototype.x = 1;
Obj.prototype.o = {x: 1};
Expected:
> new Obj
x: 1, o.x: 1
> new Obj
x: 1, o.x: 1
> new Obj
x: 1, o.x: 1
Actual:
> new Obj
x: 1, o.x: 1
> new Obj
x: 1, o.x: 2
> new Obj
x: 1, o.x: 3
So, it seems that if a prototypal property is a reference type, then it is shared across all instances, yet if it’s a non-reference type then it is reinitialized for each instance; to confirm this hypothesis, I tested a few other types, such as string (which behaves like number) and array (which behaves like object).
I have determined that I can avoid this pitfall if I reinitialize the object property in the ctor, like this:
function Obj() {
this.o = {x: 1};
}
Which just seems really unorthodox (that I’d be required to manually reinitialize properties but only if they’re a reference object).
Can anyone shed some light on what’s going on?
Think of it this way. You’re always going to be modifying the object on which you’re operating, irrespective of where the value comes from.
When you do this for the first time:
It is getting the value of
xfromObj.prototype.xbecause there was noxproperty directly on thethisobject, nevertheless the++is still operating on thethisobject, so the value is set on that object. That value is now directly on the instance for future modifications. (Theprototype.xis shadowed until the direct property isdeleted.)However, when you do this:
The only operation on the
thisobject is the lookup ofo. Since again there is nooproperty onthis, you’ll get the reference to the object stored atObj.prototype.o. At this point, there’s no actual modification of thethisobject.After the reference is returned, you then look up the property
xon theObj.prototype.oobject, and modify it.So it’s actually quite consistent. With
this.o.x++you’ve performed a lookup onthis, but no mutation. The mutation is on the referenced object. But withthis.x++, you’re mutating the object directly.So yes, unless you want your reference types to be shared among all instances created from the constructor, you should put the reference types directly on the instances and not on the
.prototype.