Understanding JavaScript Prototype Chain Easily

2017. 2. 21.

Understanding JavaScript Prototype Chain Easily

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:

  1. Check if attr1 exists on object c. → No.
  2. Check if c.__proto__ exists. → Yes.
  3. Move to the object referenced by c.__proto__ (i.e., b). → Check again.
  4. 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.

♥ Support writer ♥
with kakaopay

Creative Commons LicenseThis work is licensed under a Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License.

shiren • © 2025Sungho Kim