Search code examples
javascriptinheritanceprototypeprototypal-inheritanceprototype-programming

Properties on a prototype being affected differently


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?


Solution

  • 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>