6

I've been staring at this answer for a while and I can't wrap my head around it: https://stackoverflow.com/a/23699009/3658800.

To summarize:


Only property reads search the prototype chain, not writes. So when you set

myObject.prop = '123';

It doesn't look up the chain, but when you set

myObject.myThing.prop = '123';

there's a subtle read going on within that write operation that tries to look up myThing before writing to its prop. So that's why writing to object.properties from the child gets at the parent's objects.


I'm basically asking someone to elaborate on this "subtle read" operation. Is myObject.myThing evaluated first, returning a reference to the myThing object (which then has its "prop" property set)? Is there some source where I can substantiate this (Mozilla, Javascript source code, etc)?

Community
  • 1
  • 1
user3658800
  • 446
  • 3
  • 12
  • 1
    js will always look up all the way to the right, minus one dot. when it can't do that, you get an err; `document.xx` is ok to try to read, because the 2nd to right segment is legit. `document.xx.yy` will `throw` because `document.xx` is _not_ legit. The same applies to any dot path, be it all props, all protos, or a mix. It's pretty obvious why setting a prototype from an instance's path would be a bad idea, so JS only uses prototype to init and facade. – dandavis Jan 11 '17 at 04:55
  • The simple answer is "because they just don't." Setting the value of a property on an object always just affects the target object. – Pointy Jan 11 '17 at 05:04
  • 1
    I think what that answer is talking about are [issues when inheriting from nested objects](https://stackoverflow.com/questions/10131052/crockfords-prototypal-inheritance-issues-with-nested-objects/), i.e. when `.myThing` is inherited it will be shared (unexpectedly). – Bergi Jan 11 '17 at 05:32

1 Answers1

4

there's a subtle read going on within that write operation that tries to look up myThing before writing to its prop. So that's why writing to object.properties from the child gets at the parent's objects.

It's not a "subtle" read. It's a regular read.

obj.prop1.prop2 = "foo"

Is interpreted as (obj.prop1).prop2 = "foo". Assuming imaginary read and write primitives, it is executed as

write(
  read(obj, 'prop1'), 
  'prop2', 
  "foo")

if that makes it clearer. In other words, first, prop1 is read from obj. consulting the prototype chain as is normal for reads. If it is found not on the instance, but on the prototype chain, then the result is the prop1 on the prototype.

Next, prop2 is set on the object resulting from obj.prop1. If that happens to be the prop1 on the prototype, then property prop2 is set on that prop1 on the prototype. If prop1 was found on the instance, then property prop2 is set on that prop1 on the instance.

There is no change here in the basic principle that reads consult the prototype chain, and writes are always on the object being written to (without reference to the prototype chain).

Notice that there can be subtle differences in this behavior in the presence of setters and getters on prototype.

To avoid confusing yourself and others, it is best to avoid keeping data on the prototype. Think of the prototype as exclusively, or primarily, as a way to share methods on the objects created based on that prototype. Use of prototypes to keep data should ideally be limited to truly constant data that is shared among instances.