JavaScript Prototype Chain

Prototype based JavaScript implements object attributes differently than class based systems. To resolve property values, JavaScript engines traverse the prototype chain. That is, the engine first checks the object for a property, if its not there it checks the prototype, and if not there, it checks the prototype’s prototype and so on and so forth. The prototype chain terminates when the engine reaches the generic Object prototype. If the requested property is not found along the prototype chain,
undefined is returned. The traversal of the prototype chain is similar to the traversal of the scope chain to find a variable definition. (See Scope Chain).

Example 1. Manually traversing the prototype chain via __proto__

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
var proto = {
  school: "Karate"
};

var makeSensai = function(name, style){
  var sensai = Object.create(proto)
  sensai.name = name;
  sensai.style = style;
  return sensai;
};

var sensaiIchi = makeSensai("Maruchan Daimondo", "Uechi Ryu");

// The entire object, including attributes of the prototype
// {"school": "Karate", name: "Maruchan Daimondo", "style": "Uechi Ryu"}
console.log(sensaiIchi);

// Just the prototype attributes
// {"school": "Karate"}
console.log(sensaiIchi.__proto__);

// Prototype of the prototype.
// Since one wasn't set, the prototype of the prototype is the generic object prototype
// The generic object prototype is represented as empty curly braces
// {}
console.log(sensaiIchi.__proto__.__proto__);

// The generic object prototype has no prototype
// null
console.log(sensaiIchi.__proto__.__proto__.__proto__);

// Finally, attempting to get the prototype of null is an error
// Uncaught TypeError: Cannot read property '__proto__' of null
console.log(sensaiIchi.__proto__.__proto__.__proto__.__proto__);

Requesting sensaiIchi.name will return “Maruchan Daimondo”, which JavaScript will find on the root object. However, requesting sensaiIchi.school will require the JavaScript engine to traverse one level up the prototype chain to return the value “Karate”. If we request sensaiIchi.toString() we will get the string “[object Object]” because the base Object prototype defines toString(). Lastly, making a request for sensaiIchi.rank will return undefined, for the property doesn’t exist anywhere in the prototype chain.

Example 2. Prototype chain

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
sensaiIchi
/**************************************
{
  name: 'Maruchan Daimondo',
  style: 'Uechi Ryu',
  __proto: {
    school: 'Karate'
    __proto__: {
      toString: function(){...}
    }
  }
}
**************************************/

 
sensaiIchi.name
// name is directly accessible on sensaiIchi
/**************************************
{
  name: 'Maruchan Daimondo', <---- Here it is
  style: 'Uechi Ryu',
  __proto: {
    school: 'Karate'
    __proto__: {
      toString: function(){...}
    }
  }
}
**************************************/


sensaiIchi.toString
// toString is not accessible on the root object
// nor is it available on its prototype
// Thus, the JS engine looks at the prototype's prototype
// In this case it happens to be the base JavaScript object
/**************************************
{
  name: 'Maruchan Daimondo',
  style: 'Uechi Ryu',
  __proto: {
    school: 'Karate'
    __proto__: {
      toString: function(){...} <---- Here it is
    }
  }
}
**************************************/

 
sensaiIchi.rank
// rank is not defined on the object, sensaiIchi
// nor is it defined on the prototype
// nor the prototype's prototype, which is the base JavaScript object, {}
// therefore the value is undefined
/**************************************
{ <-- Not here...
  name: 'Maruchan Daimondo',
  style: 'Uechi Ryu',
  __proto: { <-- nor here...
    school: 'Karate'
    __proto__: { <-- nor here! undefined is returned
      toString: function(){...}
    }
  }
}
**************************************/

Changing the value on an attribute set by the prototype is another way we may demonstrate the prototype chain.

Example 3. Overwriting the prototype

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
var proto = {
  school: 'Karate'
};

var makeSensai = function(name, style) {
  var sensai = Object.create(proto);
  sensai.name = name;
  sensai.style = style;
  return sensai
}

var sensaiIchi = makeSensai('Maruchan Daimondo', 'Uechi Ryu');

// Both of these log 'Karate'
// sensaiIchi.school isn't found on the on the sensaiIchi
// object, so the prototype is inspected and its found.
console.log(sensaiIchi.school);
console.log(sensaiIchi.__proto__.school);

// Set the object's school property to 'Kendo'
sensaiIchi.school = 'Kendo';

// Outputs 'Kendo'
// And confirms the value was set to 'Kendo' on the object
console.log(sensaiIchi.school);

// Outputs 'Karate'
// So the prototype of the sensaiIchi object is unscathed
console.log(sensaiIchi.__proto__.school);

// Get the school attribute back to the value on the prototype
// By deleting the attribute from the object
delete sensaiIchi.school;

// Now, again, both instructions log 'Karate'
console.log(sensaiIchi.school);
console.log(sensaiIchi.__proto__.school);

Example 4. Prototype mutations

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
var proto = {
  school: 'Karate'
};

var makeSensai = function(name, style) {
  var sensai = Object.create(proto);
  sensai.name = name;
  sensai.style = style;
  return sensai;
};
var sensaiIchi = makeSensai('Maruchan Daimondo', 'Uechi Ryu');
var sensaiNi = makeSensai('Kajikawa Ryoji', 'Goju Ryu');
   
// Both instructions log 'Karate'
console.log(sensaiIchi.school);
console.log(sensaiNi.school);

// Changing the prototype
proto.school = 'Kendo';

// Both instructions log 'Kendo'
console.log(sensaiIchi.school);
console.log(sensaiNi.school);

This behavior is potentially dangerous and has good and bad points. Thus, its important to keep in mind when programming with JavaScript.

Reference:
Mikowski, Michael S., and Josh C. Powell. Single Page Web Applications: JavaScript End-to-end. Shelter Island, NY: Manning, 2013. Print.