JavaScript Object Prototypes: A Complete Tutorial with Examples

In JavaScript, every object has a prototype. A prototype is a blueprint from which objects inherit properties and methods. Prototypes allow you to create objects that share properties and methods without having to redefine them every time.

This mechanism is the core of prototype-based inheritance in JavaScript.

What is an Object Prototype?

When you create an object in JavaScript, it automatically has a property called __proto__ (also accessible via Object.getPrototypeOf(obj)), which points to its prototype.

If a property or method is not found in the object itself, JavaScript looks for it in the prototype.

1. Creating an Object with a Prototype

When you create an object using an object l
iteral or constructor function, JavaScript automatically assigns the prototype.

Example 1: Default Prototype of an Object

let car = {
  brand: 'Toyota',
  model: 'Corolla',
};

console.log(car.__proto__); // Output: {}

Explanation

The car object has a prototype (inherited from Object.prototype) that contains default methods like .toString() and .hasOwnProperty().

2. Understanding Object.prototype

Every object in JavaScript inherits from Object.prototype unless explicitly defined otherwise. This is why all objects in JavaScript have access to methods like .toString() and .hasOwnProperty().

Example 2: Using a Method from Object.prototype

let person = {
  name: 'Alice',
};

console.log(person.hasOwnProperty('name')); // Output: true
console.log(person.toString()); // Output: [object Object]

Explanation

The person object can use hasOwnProperty() and toString() because they are inherited from Object.prototype.

3. Creating Custom Prototypes Using Constructor Functions

You can create a custom prototype by using a constructor function. The properties and methods added to the constructor’s prototype are shared among all instances created from that constructor.

Example 3: Creating a Prototype Using a Constructor Function

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

// Adding a method to the prototype
Person.prototype.greet = function() {
  return `Hello, my name is ${this.name}.`;
};

let john = new Person('John', 30);
let jane = new Person('Jane', 25);

console.log(john.greet()); // Output: Hello, my name is John.
console.log(jane.greet()); // Output: Hello, my name is Jane.

Explanation

The Person constructor function initializes name and age properties.
Person.prototype.greet adds a method to the Person prototype.
All instances (john and jane) have access to the greet method because they inherit from Person.prototype.

4. Adding Properties and Methods to an Existing Prototype

You can add properties and methods to an existing prototype even after objects have been created.

Example 4: Adding Methods to the Prototype

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

let car1 = new Car('Toyota', 'Camry');

// Add a method to the prototype
Car.prototype.getDetails = function() {
  return `${this.brand} ${this.model}`;
};

console.log(car1.getDetails()); // Output: Toyota Camry

Explanation

The Car constructor function initializes the brand and model properties.
We added a getDetails method to Car.prototype.
car1 can access getDetails() because it was added to the prototype.

5. Overriding Prototype Properties and Methods

If an object has a property or method with the same name as one on its prototype, it overrides the prototype's version.

Example 5: Overriding a Prototype Method

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

Animal.prototype.speak = function() {
  return `${this.name} makes a noise.`;
};

let dog = new Animal('Dog');
console.log(dog.speak()); // Output: Dog makes a noise.

// Overriding the speak method for the dog object
dog.speak = function() {
  return `${this.name} barks.`;
};

console.log(dog.speak()); // Output: Dog barks.

Explanation

The Animal constructor has a speak method on its prototype.
We override the speak method for the dog object, changing its behavior.

6. Prototypal Inheritance

You can create prototypes that inherit from other prototypes, forming a prototype chain.

Example 6: Inheriting Prototypes

function Vehicle(type) {
  this.type = type;
}

Vehicle.prototype.start = function() {
  return `${this.type} is starting.`;
};

function Bike(type, brand) {
  Vehicle.call(this, type); // Inherit properties
  this.brand = brand;
}

// Inherit methods
Bike.prototype = Object.create(Vehicle.prototype);
Bike.prototype.constructor = Bike;

Bike.prototype.getBrand = function() {
  return `${this.brand}`;
};

let myBike = new Bike('Motorcycle', 'Harley-Davidson');

console.log(myBike.start()); // Output: Motorcycle is starting.
console.log(myBike.getBrand()); // Output: Harley-Davidson

Explanation

The Bike constructor calls Vehicle.call(this, type) to inherit properties from Vehicle.
Bike.prototype = Object.create(Vehicle.prototype); sets up the prototype chain so that Bike inherits methods from Vehicle.
Bike.prototype.constructor = Bike; restores the constructor reference on Bike.prototype.

7. Checking an Object's Prototype

You can check the prototype of an object using Object.getPrototypeOf() or the __proto__ property.

Example 7: Checking Prototypes

let car = {
  brand: 'Toyota',
};

console.log(Object.getPrototypeOf(car) === Object.prototype); // Output: true
console.log(car.__proto__ === Object.prototype); // Output: true

Explanation

Object.getPrototypeOf(car) and car.__proto__ both point to Object.prototype.

8. Using hasOwnProperty() to Check for Own Properties

To check if a property is directly on an object and not on its prototype chain, use hasOwnProperty().

Example 8: Using hasOwnProperty()

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

Animal.prototype.speak = function() {
  return `${this.name} makes a noise.`;
};

let cat = new Animal('Cat');

console.log(cat.hasOwnProperty('name')); // Output: true
console.log(cat.hasOwnProperty('speak')); // Output: false

Explanation

cat.hasOwnProperty(‘name') returns true because name is a direct property of cat.
cat.hasOwnProperty(‘speak') returns false because speak is a method on Animal.prototype.

Summary

JavaScript prototypes are a powerful mechanism for inheritance and code reuse. Here’s a recap of the key points:

Key Concepts

  • Prototypes: Each JavaScript object has a prototype from which it can inherit properties and methods.
  • Constructor Functions: Prototypes can be defined for constructor functions to share methods across instances.
  • Inheritance: Objects can inherit from other objects, forming a prototype chain.
  • __proto__ and Object.getPrototypeOf(): Use these to access an object's prototype.
  • hasOwnProperty(): Checks if a property belongs directly to the object, not its prototype.

Practical Use Cases

  • Code Reusability: Define shared methods on prototypes instead of directly on objects.
  • Inheritance: Create objects that inherit properties and methods from other objects using constructor functions and prototypes.

Mastering prototypes allows you to write more efficient and flexible JavaScript code, especially when working with object-oriented programming in JavaScript.

Related posts

JavaScript for…of Loop: A Comprehensive Tutorial

JavaScript Promises: A Complete Tutorial

JavaScript for…in Loop: A Complete Tutorial