Exploring JavaScript Prototypes and Prototype Chain

Photo by Maria Lysenko on Unsplash

JavaScript is a versatile and powerful language that offers unique features not found in many other programming languages. One of the fundamental concepts that underpin its object-oriented capabilities is the prototype system. Understanding prototypes and the prototype chain is essential for mastering JavaScript, as it allows developers to grasp how inheritance and property resolution work in the language. This article will delve into the details of JavaScript prototypes and the prototype chain, providing clear explanations and practical examples to illustrate these concepts.

What is a Prototype?

In JavaScript, every object has a prototype. A prototype is another object from which the first object inherits properties. When you attempt to access a property on an object, JavaScript will first look for the property on the object itself. If it doesn’t find it there, it will look at the object’s prototype. This chain of looking up properties continues until JavaScript finds the property or reaches the end of the prototype chain (which is null).

Creating Objects with Prototypes

When you create an object using an object literal or the new keyword with a constructor function, JavaScript automatically assigns a prototype to the new object.

function Person(name) {
this.name = name;
}

Person.prototype.greet = function() {
return `Hello, my name is ${this.name}`;
};
const alice = new Person('Alice');
console.log(alice.greet()); // Output: Hello, my name is Alice

In this example, Person.prototype is the prototype of the alice object. The greet method is defined on the Person.prototype, so when greet is called on alice, JavaScript looks up the prototype chain to find the method.

The Prototype Chain

The prototype chain is a series of links between objects that JavaScript follows to resolve property and method references. Each object has a reference to its prototype, and this reference forms the chain.

Prototype Chain Example

Consider the following example to understand the prototype chain better:

function Animal(voice) {
this.voice = voice;
}

Animal.prototype.speak = function() {
return this.voice;
};
function Dog(name, voice) {
Animal.call(this, voice);
this.name = name;
}
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;
Dog.prototype.bark = function() {
return `${this.name} says ${this.voice}`;
};
const rover = new Dog('Rover', 'Woof');
console.log(rover.bark()); // Output: Rover says Woof
console.log(rover.speak()); // Output: Woof

In this example, Dog inherits from Animal. The Dog.prototype is set to a new object created with Animal.prototype as its prototype. This means that rover, an instance of Dog, has access to methods defined on both Dog.prototype and Animal.prototype due to the prototype chain.

Understanding __proto__ and prototype

There are two commonly confused properties in JavaScript’s prototype system: __proto__ and prototype.

  • __proto__ is an internal property that points to the prototype of the object. It’s part of the instance.
  • prototype is a property of constructor functions, not of the instance, and it is used to build the __proto__ of instances created by that constructor.

Example

function Car(model) {
this.model = model;
}

Car.prototype.getModel = function() {
return this.model;
};
const tesla = new Car('Tesla Model 3');
console.log(tesla.__proto__ === Car.prototype); // Output: true
console.log(Car.prototype.constructor === Car); // Output: true
console.log(tesla.getModel()); // Output: Tesla Model 3

In this example, tesla.__proto__ is Car.prototype, and Car.prototype has a constructor property that points back to Car. This relationship is what allows instances of Car to inherit methods from Car.prototype.

Advantages of Using Prototypes

Using prototypes in JavaScript has several advantages:

  1. Memory Efficiency: Methods are shared among instances, reducing memory usage.
  2. Performance: Method lookups are faster because they follow the prototype chain.
  3. Dynamic Method Addition: You can add methods to prototypes at any time, and all instances will have access to these methods.

Dynamic Method Addition Example

function Cat(name) {
this.name = name;
}
const Kitty = new Cat('Kitty');

Cat.prototype.meow = function() {
return `${this.name} says Meow`;
};
console.log(kitty.meow()); // Output: Kitty says Meow
Cat.prototype.purr = function() {
return `${this.name} is purring`;
};
console.log(kitty.purr()); // Output: Kitty is purring

Here, the purr method is added to Cat.prototype after the kitty instance is created, but kitty can still access the purr method because it follows the prototype chain.

Understanding JavaScript prototypes and the prototype chain is crucial for writing efficient and effective code. Prototypes provide a powerful mechanism for inheritance and property sharing, enabling developers to create memory-efficient and performant applications. By mastering these concepts, you can take full advantage of JavaScript’s object-oriented features and write more sophisticated and maintainable code. Whether you’re adding methods dynamically or creating complex inheritance structures, prototypes are a fundamental part of JavaScript that every developer should be well-versed in.


Exploring JavaScript Prototypes and Prototype Chain was originally published in CarlosRojasDev on Medium, where people are continuing the conversation by highlighting and responding to this story.

Scroll to Top