3

I'm using prototype inheritance in this code snippet:

function SuperType() {
  this.colors = ["red", "blue", "green"];
  this.x = 1;
}

function SubType() {}
SubType.prototype = new SuperType();

var instance1 = new SubType();
instance1.colors.push("black");
instance1.x = 2;
//alert(instance1.colors); // "red,blue,green,black"
//alert(instance1.x); // 2

var instance2 = new SubType();
alert(instance2.colors); // "red,blue,green,black"
alert(instance2.x); // 1

I expect the output to be

"red,blue,green"
1

or

"red,blue,green,black"
2

but I get:

"red,blue,green,black"
1

Why?

Michael Laszlo
  • 12,009
  • 2
  • 29
  • 47
arachide
  • 8,006
  • 18
  • 71
  • 134
  • 1
    you just created a new instance of `SubType`. Try `var instance2 = instance1` – Cyval Dec 27 '15 at 23:46
  • 1
    arrays and objects are passed by reference, while for primitive types (string,numbers, etc.) the reference is the value , [related](http://stackoverflow.com/questions/518000/is-javascript-a-pass-by-reference-or-pass-by-value-language) – maioman Dec 27 '15 at 23:56

3 Answers3

4

The problem is here:

SubType.prototype = new SuperType();

Because the SuperType constructor puts the .colors Array on the object, and because the object is used as the .prototype of SubType, that .colors Array is shared between all SubType instances.

Instead, don't invoke the constructor when setting up inheritance but do invoke it within the SubType constructor.

function SubType() {
    SuperType.apply(this, arguments);
}
SubType.prototype = Object.create(SuperType.prototype);

The reason the .x didn't have the same issue is that numbers are not mutable, so when you try to modify it, a new x gets created directly on the object you're using instead of mutating the .prototype.

1

When you write

instance1.x = 2;

you are adding a new property called x to instance1.

The prototype of instance1, which you can look up with instance1.__proto__, is unaffected. The value of instance1.__proto__.x is still 1.

When you refer to

instance1.x

the object's own property instance1.x takes precedence over the prototype's property instance1.__proto__.x. We say that the x on instance1 shadows the x on instance1.__proto__.

When JavaScript evaluates instance1.x, it checks the own properties of instance1 before moving up the prototype chain. Therefore, the value of the own property instance1.x is what you see.

But when you write

instance1.colors

the object instance1 does not have an own property called colors. Therefore, JavaScript looks at its prototype. It finds instance1.__proto__.colors and returns its current value.

When you wrote

instance1.colors.push("black");

you did not add a new property to instance1. You merely modified the array instance1.__proto__.colors. All objects that have the same prototype will see the same value of colors unless they have a property that shadows colors.

In the code snippet below, I have made a third object, c, that defines an own property called colors, which shadows the prototype's property c.__proto__.colors.

var c = new SubType();
c.colors = [ 'orange', 'purple' ];

The value of the own property c.colors is a different array than the prototype's property c.__proto__.colors. Objects that don't have an own property colors will continue to see the value of the prototype's colors.

function SuperType() {
    this.colors = ["red", "blue", "green"];
    this.x = 1;
}
function SubType() {}
SubType.prototype = new SuperType();

var a = new SubType();
a.colors.push("black");
a.x = 2;
message('a.colors: ' + a.colors.join(', '));  // red, blue, green, black (prototype's colors)
message('a.x: ' + a.x);                       // 2 (own property x)
message('a.__proto__.x: ' + a.__proto__.x);   // 1 (prototype's x)

var b = new SubType();
message('b.colors: ' + b.colors.join(', '));  // red, blue, green, black (prototype's colors)
message('b.x: ' + b.x);                       // 1 (prototype's x)

var c = new SubType();
// Make an own property, colors, that shadows the prototype's property.
c.colors = [ 'orange', 'purple' ];
message('c.colors: ' + c.colors.join(', '));  // orange, purple (own property colors)
message('b.colors: ' + b.colors.join(', '));  // red, blue, green, black (prototype's colors)
message('a.colors: ' + a.colors.join(', '));  // red, blue, green, black (prototype's colors)


function message(line) {
  document.getElementById('messageBox').innerHTML += line + '<br>';
}
body {
  font-family: sans-serif;
}
<div id="messageBox"></div>
Michael Laszlo
  • 12,009
  • 2
  • 29
  • 47
0

When you run .push("black") you are using a method that modifies the object (the list of colours) in place; so all variables pointing at that object see the change.

When you use = 2, you are replacing the value of that one variable, so other variables can still point at the original value.

IMSoP
  • 89,526
  • 13
  • 117
  • 169