Understanding JavaScript Prototype Chain Easily
2017. 2. 21.

JavaScript is a multi-paradigm language that supports both object-oriented programming and functional programming, treating functions as first-class citizens. Those who have worked with JavaScript know that it lacks the concept of a class, making object creation and inheritance different from other languages. Particularly, object-oriented programming (OOP) in JavaScript is mainly achieved through a mechanism called prototypes. While there are numerous articles discussing prototypes, this post focuses on the core mechanism for implementing inheritance in prototypes: the prototype chain.
Objects
In JavaScript, there are two ways to create objects: using object literals or constructors.
var objectMadeByLiteral = {};
var objectMadeByConstructor = new Object();
Literals are shortcuts for creating objects of type Object
. The second line also uses the Object
constructor, making both examples identical in terms of the structure and prototype of the created objects. Both result in objects of type Object
, allowing access to methods like hasOwnProperty
, toString
, and valueOf
. The Object
type is the top-level type for all objects. In traditional OOP languages, this code would merely create an instance of an Object
, which might not be considered inheritance. However, in JavaScript, these objects inherit from the prototype of the Object
constructor. This dynamic linking resembles a linked list rather than static class mechanisms. While this dynamic nature allows flexibility, it can lead to issues if conventions or anti-patterns are ignored.
Prototype
Using prototypes allows objects to connect and establish a one-way inheritance relationship. In JavaScript, this means sharing member functions or variables between connected objects.
var a = {
attr1: 1
}
var b = {
attr2: 2
}
Here, a
has a member variable attr1
, and b
has attr2
. Currently, there’s no way for b
to access attr1
. If we want b
to use attr1
as if it owns it, we can use the special property __proto__
.
var a = {
attr1: 'a1'
}
var b = {
attr2: 'a2'
}
b.__proto__ = a;
b.attr1 // 'a1'
The engine treats objects like hash maps with keys and values (data or functions). The core of prototype chaining lies in the engine's use of the __proto__
property, which corresponds to ECMAScript's internal property [[Prototype]]
. While modern browsers expose this for debugging convenience, developers should avoid direct access in code. Instead, use Object.getPrototypeOf()
for referencing an object's prototype.
var a = {
attr1: 'a1'
}
var b = {
attr2: 'a2'
}
b.__proto__ = a;
b.attr1 // 'a1'
a.attr1 = 'a000'; // Changing inherited content
b.attr1 // 'a000'
a.attr3 = 'a3'; // Adding inherited content
b.attr3 // 'a3'
delete a.attr1; // Deleting inherited content
b.attr1 // undefined
This dynamic linking enables changes to inheritance structures even after objects are created—a feature not possible with static class-based systems.
Prototype Identifier Lookup
JavaScript uses two mechanisms for identifier lookup: prototype lookup and scope lookup. Prototype lookup involves traversing the prototype chain dynamically during execution to find properties or methods.
var a = {
attr1: 'a'
};
var b = {
__proto__: a,
attr2: 'b'
};
var c = {
__proto__: b,
attr3: 'c'
};
c.attr1 // 'a'
Here’s how JavaScript resolves c.attr1
:
- Check if
attr1
exists on objectc
. → No. - Check if
c.__proto__
exists. → Yes. - Move to the object referenced by
c.__proto__
(i.e.,b
). → Check again. - Repeat steps until reaching an object with the required property or reaching the end of the chain (
null
).
The final link in any prototype chain is always Object.prototype
, which has no prototype itself.
Constructor
When using constructors to create objects, they automatically link the created object to the constructor’s prototype.
function Parent(name) {
this.name = name;
}
Parent.prototype.getName = function() {
return this.name;
};
var p = new Parent('myName');
Here’s what happens internally:
p = {}; // Create new object
Parent.call(p, 'myName'); // Execute Parent function with p as context
p.__proto__ = Parent.prototype; // Link prototype
p.getName(); // 'myName'
To implement inheritance between constructors like Parent and Child:
function Parent(name) {
this.name = name;
}
Parent.prototype.getName = function() {
return this.name;
};
function Child(name) {
Parent.call(this, name);
this.age = 0;
}
Child.prototype = Object.create(Parent.prototype);
Child.prototype.constructor = Child;
Child.prototype.getAge = function() {
return this.age;
};
var c = new Child();
Here, Object.create()
links Child.prototype
to Parent.prototype
, forming a chain like Child instance → Child.prototype → Parent.prototype. This ensures proper inheritance while maintaining dynamic flexibility.
ES6
To simplify prototypal inheritance implementation, ECMAScript 6 introduced the class
syntax:
class Parent {
constructor(name) {
this.name = name;
}
getName() {
return this.name;
}
}
class Child extends Parent {
constructor(name) {
super(name); // Use super instead of constructor borrowing
this.age = 0;
}
getAge() {
return this.age;
}
}
While syntactically different, ES6 classes still rely on prototypes under the hood.
Conclusion
This post explored how prototype chains work in JavaScript, how identifiers are resolved through them, and how they can be created using constructors or ES6 classes. Understanding these concepts is crucial for mastering JavaScript’s unique approach to inheritance and writing efficient code.
with kakaopay
Recommend Post
This work is licensed under a Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License.