Deep Dive into JavaScript Prototypes and Prototype Chains
JavaScript is a prototype-based language. Understanding Prototypes and the Prototype Chain is crucial for mastering JavaScript. This article will take you deep into this core mechanism.
Core Concepts
In JavaScript, almost all objects are created by functions. We need to distinguish between two important properties:
prototype: This is a property specific to functions. It points to an object that contains properties and methods shared by instances created by that constructor function.__proto__(or[[Prototype]]): This is a property specific to objects (instances). It points to the prototype object of the constructor that created the object.
In short: instance.__proto__ === Constructor.prototype
How the Prototype Chain Works
When you access a property of an object, the JavaScript engine looks it up in the following order:
- First, it checks if the object itself has the property.
- If not, it looks in the object's
__proto__(i.e., the constructor'sprototype). - If still not found, it looks in the
__proto__of that__proto__. - This process continues until it reaches
null(the end of the prototype chain).
If it's still not found, it returns undefined.
function Person(name) {
this.name = name;
}
Person.prototype.sayHello = function() {
console.log(`Hello, I am ${this.name}`);
};
const p1 = new Person('Alice');
// Accessing own property
console.log(p1.name); // "Alice"
// Accessing method on prototype
p1.sayHello(); // "Hello, I am Alice"
// Verifying prototype relationship
console.log(p1.__proto__ === Person.prototype); // true
console.log(Person.prototype.__proto__ === Object.prototype); // true
console.log(Object.prototype.__proto__ === null); // true
Relationship between Constructor, Prototype, and Instance
- Constructor:
function Foo() {} - Prototype Object:
Foo.prototype - Instance:
var f = new Foo()
Their relationship is as follows:
f.__proto__ === Foo.prototypeFoo.prototype.constructor === Foo
Implementing Inheritance
ES5 Prototype Inheritance
Before ES6, we usually used the prototype chain to implement inheritance:
function Animal(type) {
this.type = type;
}
Animal.prototype.eat = function() {
console.log('Eating...');
};
function Dog(name) {
Animal.call(this, 'Canine'); // Borrow constructor to inherit instance properties
this.name = name;
}
// Establish prototype chain for method inheritance
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog; // Fix constructor pointer
Dog.prototype.bark = function() {
console.log('Woof!');
};
const myDog = new Dog('Buddy');
myDog.eat(); // Inherited from Animal
myDog.bark(); // Dog's own method
ES6 Class Syntax Sugar
ES6 introduced the class keyword, but it is just syntactic sugar; the underlying mechanism is still the prototype chain.
class Animal {
constructor(type) {
this.type = type;
}
eat() {
console.log('Eating...');
}
}
class Dog extends Animal {
constructor(name) {
super('Canine');
this.name = name;
}
bark() {
console.log('Woof!');
}
}
const myDog = new Dog('Max');
myDog.eat();
Summary
- JavaScript objects inherit properties and methods through the prototype chain.
prototypeis a property of the constructor function, used to define shared methods.__proto__is a property of the instance, pointing to the constructor's prototype.Object.prototypeis the end of the prototype chain, and its__proto__isnull.- Understanding the prototype chain helps in writing more efficient and reusable code.