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:
this.x++;
It is getting the value of x
from Obj.prototype.x
because there was no x
property directly on the this
object, nevertheless the ++
is still operating on the this
object, so the value is set on that object. That value is now directly on the instance for future modifications. (The prototype.x
is shadowed until the direct property is delete
d.)
However, when you do this:
this.o.x++
The only operation on the this
object is the lookup of o
. Since again there is no o
property on this
, you'll get the reference to the object stored at Obj.prototype.o
. At this point, there's no actual modification of the this
object.
After the reference is returned, you then look up the property x
on the Obj.prototype.o
object, and modify it.
So it's actually quite consistent. With this.o.x++
you've performed a lookup on this
, but no mutation. The mutation is on the referenced object. But with this.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
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With