0

I want to have an object that inherits an array property and a method to add elements to the inherited array. However, the inherited method yChange() changes the prototype array and not the inherited array. This question explains why the undesired behavior happens. But can't figure out how to get the desired behavior.

var parent = {
    x: 0,
    y: [],
    xChange: function () {this.x += 1},
    yChange: function () {this.y.push(1)}
};

var child = Object.create(parent);
child.xChange();
child.yChange();

console.log(child.x, child.y); // 1 [1]
console.log(parent.x, parent.y); // 0 [1]

Desired:

console.log(child.x, child.y); // 1 [1]
console.log(parent.x, parent.y); // 0 []
Community
  • 1
  • 1
David Prentiss
  • 548
  • 1
  • 6
  • 16

2 Answers2

5

However, the inherited method yChange() changes the prototype array and not the inherited array.

There is no difference between the "inherited" array and the "prototype" array. They are one and the same.

You have to give child its own array:

var child = Object.create(parent);
child.y = [];

So, I can't inherit an 'own' array as with the number? The question is how to do it with an inherited array.

Everything that is inherited is not "owned" by the child. Even numbers. The difference is that numbers are not mutable, hence the issue is not apparent.

Look closely what here:

this.x += 1

You are assigning a new value to this.x. This will create child.x, not modify parent.x.

Lets look at

this.y.push(1);

You are not assigning anything here. You are reading this.y, which resolves to parent.y and you are mutating the array object itself.

Is it clearer now why you have to assign a new array to child.y (child.y = [];) ? The assignment is what gives the child its own copy of the data.

The difference between the number and array case is that numbers are immutable and arrays are mutable. The immutability of numbers forces you to create a new number and assign it.

Not so with mutable values. You have to explicitly create a copy of the value if you don't want it to be shared (and that's what child.y = []; is basically doing).

Felix Kling
  • 795,719
  • 175
  • 1,089
  • 1,143
  • So, I can't inherit an 'own' array as with the number? The question is how to do it with an inherited array. – David Prentiss Jun 21 '15 at 19:18
  • 1
    *Everything* that is inherited is **not** "owned" by the child. Even numbers. The difference is that numbers are *not mutable*, hence the issue is not apparent. – Felix Kling Jun 21 '15 at 19:20
  • You might want to read the linked question again, I feel like you haven't really understood the *why* (ha, I answered that one... interesting, maybe I didn't do such a good job). – Felix Kling Jun 21 '15 at 19:21
  • Yeah, my question is most definitely, "how?" – David Prentiss Jun 21 '15 at 19:22
  • 1
    @Dave: Then the answer is: "you can't". You can't magically make a single array (`parent.y`) act like it was two. – Felix Kling Jun 21 '15 at 19:22
  • I added some more information, I hope it's clearer now. – Felix Kling Jun 21 '15 at 19:29
  • @FelixKling: I think you did a fine job… But yes I know the feeling of finding old answers of oneself :-) – Bergi Jun 21 '15 at 19:37
  • `xChange: function () {this.x = this.x + 1}` yields the same results. Does that mean `this.x` refers to `parent.x` on the right side and `child.x` on the left? – David Prentiss Jun 21 '15 at 19:37
  • @Bergi: Thanks Bergi :) (and for the improvements) – Felix Kling Jun 21 '15 at 19:37
  • @Dave: Yes, at least the first time it is called. After that it will always refer to `child.x`. – Felix Kling Jun 21 '15 at 19:38
  • Thanks @FelixKling. I guess "inheritance" with object create is totally senseless to me. – David Prentiss Jun 21 '15 at 19:53
  • @Dave: At least not if you need instance specific data, yeah. That's the problem that constructor functions solve. `new Foo()` is basically doing this (simplified): `var newObj = Object.create(Foo.prototype); Foo.apply(newObj, arguments); return newObj;`. And the `Foo.apply(...)` part is where all the instance specific initialization happens. – Felix Kling Jun 21 '15 at 19:57
1

Felix is right about an assignment being necessary to change child.y. In your example, you could check to see if the memory addresses are the same first and then assign a new one for the new instance if they match. Like so:

var parent = {
    x: 0,
    y: [],
    xChange: function () {this.x += 1},
    yChange: function () {
        if (this.y == Object.getPrototypeOf(this).y)
            this.y = new Array()
        this.y.push(1)
    }
};

var child = Object.create(parent);
child.xChange();
child.yChange();

console.log(child.x, child.y); // 1 [1]
console.log(parent.x, parent.y); // []
Jpaji Rajnish
  • 1,491
  • 4
  • 17
  • 35
  • 1
    If one expects to be able to do `parent.yChange`, then the condition should be extended by `this !== parent && ...` – Felix Kling Jun 21 '15 at 19:40
  • I think I'll just have to use a constructor. Thanks @JpajiRajnish. – David Prentiss Jun 21 '15 at 19:47
  • I realized after writing this that you'd have a problem if you wanted to inherit from `child`. To solve this you can call the prototype of the instance instead of `parent` explicitly and check the memory address of that. (answer is edited) – Jpaji Rajnish Jun 21 '15 at 19:51