Skip to main content

Prototypes and Prototype Chain

Created: December 18, 2025 4 min read

Prototypes are the mechanism by which JavaScript objects inherit features from one another. Understanding prototypes is essential for JavaScript OOP.

What is a Prototype?

Every JavaScript object has a prototype, which is another object:

const obj = {};
console.log(Object.getPrototypeOf(obj)); // Object.prototype
console.log(obj.__proto__); // Object.prototype (deprecated but still works)

Prototype Chain

Objects inherit properties from their prototype, which can inherit from its prototype:

const grandparent = { name: "Grandparent" };
const parent = Object.create(grandparent);
parent.age = 50;

const child = Object.create(parent);
child.hobby = "coding";

console.log(child.hobby); // "coding" (own property)
console.log(child.age); // 50 (from parent)
console.log(child.name); // "Grandparent" (from grandparent)

Object.create()

Create an object with a specific prototype:

const proto = {
    greet() {
        return `Hello, ${this.name}!`;
    }
};

const person = Object.create(proto);
person.name = "Alice";

console.log(person.greet()); // "Hello, Alice!"

Constructor Functions

Functions used with new keyword to create objects:

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

Person.prototype.greet = function() {
    return `Hello, I'm ${this.name}`;
};

const alice = new Person("Alice", 30);
console.log(alice.greet()); // "Hello, I'm Alice"

How new Works

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

// new does this:
// 1. Create empty object
// 2. Set object's prototype to Person.prototype
// 3. Call Person with this = object
// 4. Return object

const person = new Person("Alice");

Prototype Methods

Object.getPrototypeOf()

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

const dog = new Animal("Buddy");
console.log(Object.getPrototypeOf(dog) === Animal.prototype); // true

Object.setPrototypeOf()

const proto = { greet() { return "Hello"; } };
const obj = {};

Object.setPrototypeOf(obj, proto);
console.log(obj.greet()); // "Hello"

Object.getOwnPropertyNames()

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

Person.prototype.age = 30;

const person = new Person("Alice");
console.log(Object.getOwnPropertyNames(person)); // ["name"]
console.log(person.age); // 30 (from prototype)

Checking Prototype

hasOwnProperty()

const person = Object.create({ age: 30 });
person.name = "Alice";

console.log(person.hasOwnProperty("name")); // true
console.log(person.hasOwnProperty("age")); // false

in Operator

const person = Object.create({ age: 30 });
person.name = "Alice";

console.log("name" in person); // true
console.log("age" in person); // true

instanceof

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

const dog = new Animal("Buddy");
console.log(dog instanceof Animal); // true
console.log(dog instanceof Object); // true

Prototype Inheritance

Single Inheritance

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

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

function Dog(name, breed) {
    Animal.call(this, name);
    this.breed = breed;
}

// Set up inheritance
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;

Dog.prototype.bark = function() {
    return `${this.name} barks`;
};

const dog = new Dog("Buddy", "Golden Retriever");
console.log(dog.speak()); // "Buddy makes a sound"
console.log(dog.bark()); // "Buddy barks"

Prototype Chain Example

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

Vehicle.prototype.start = function() {
    return `${this.brand} started`;
};

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

Car.prototype = Object.create(Vehicle.prototype);
Car.prototype.constructor = Car;

Car.prototype.drive = function() {
    return `${this.brand} ${this.model} is driving`;
};

const car = new Car("Toyota", "Camry");
console.log(car.start()); // "Toyota started"
console.log(car.drive()); // "Toyota Camry is driving"

Practical Examples

Extending Built-in Objects

// Add method to Array prototype
Array.prototype.first = function() {
    return this[0];
};

Array.prototype.last = function() {
    return this[this.length - 1];
};

const arr = [1, 2, 3, 4, 5];
console.log(arr.first()); // 1
console.log(arr.last()); // 5

Creating a Plugin System

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

Plugin.prototype.execute = function() {
    return `${this.name} executed`;
};

function AuthPlugin(name) {
    Plugin.call(this, name);
}

AuthPlugin.prototype = Object.create(Plugin.prototype);
AuthPlugin.prototype.constructor = AuthPlugin;

AuthPlugin.prototype.authenticate = function(user) {
    return `${this.name} authenticated ${user}`;
};

const auth = new AuthPlugin("Auth");
console.log(auth.execute()); // "Auth executed"
console.log(auth.authenticate("Alice")); // "Auth authenticated Alice"

Mixin Pattern

const canEat = {
    eat() { return `${this.name} is eating`; }
};

const canWalk = {
    walk() { return `${this.name} is walking`; }
};

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

// Mix in methods
Object.assign(Person.prototype, canEat, canWalk);

const person = new Person("Alice");
console.log(person.eat()); // "Alice is eating"
console.log(person.walk()); // "Alice is walking"

Common Patterns

Prototype Delegation

const parent = {
    greet() { return "Hello"; }
};

const child = Object.create(parent);
child.name = "Alice";

console.log(child.greet()); // "Hello"

Concatenative Inheritance

function createAnimal(name) {
    return {
        name,
        speak() { return `${this.name} speaks`; }
    };
}

function createDog(name, breed) {
    const animal = createAnimal(name);
    return Object.assign(animal, {
        breed,
        bark() { return `${this.name} barks`; }
    });
}

const dog = createDog("Buddy", "Golden");
console.log(dog.speak()); // "Buddy speaks"
console.log(dog.bark()); // "Buddy barks"

Performance Considerations

Prototype Lookup

// Prototype lookup is slower than own properties
const obj = { a: 1 };
Object.setPrototypeOf(obj, { b: 2 });

// Accessing obj.a is faster than obj.b
// because obj.a is an own property

Avoid Modifying Built-in Prototypes

// Bad - modifies built-in prototype
Array.prototype.myMethod = function() { };

// Good - create your own class
class MyArray extends Array {
    myMethod() { }
}

Summary

  • Prototype: object that other objects inherit from
  • Prototype chain: chain of prototypes for inheritance
  • Constructor function: function used with new keyword
  • Object.create(): create object with specific prototype
  • Inheritance: set up with Object.create() and call()
  • hasOwnProperty(): check if property is own
  • instanceof: check if object is instance of constructor
  • Best practice: use classes instead of prototypes (ES6+)

Official Documentation

Next Steps

  1. Constructor Functions and the ’new’ Keyword
  2. ES6 Classes: Syntax and Features
  3. Inheritance: Extends and Super

Resources

Comments

Share this article

Scan to read on mobile