Skip to main content

Prototypal Inheritance Explained

Answer​

JavaScript uses prototypal inheritance, where objects inherit directly from other objects. Every object has an internal link to another object called its prototype.

Prototype Chain​

How It Works​

// When accessing a property:
const person = {
name: "John",
greet() {
return `Hello, I'm ${this.name}`;
},
};

const employee = Object.create(person);
employee.role = "Developer";

console.log(employee.role); // "Developer" (own property)
console.log(employee.name); // "John" (from prototype)
console.log(employee.greet()); // "Hello, I'm John"

// Check where property comes from
console.log(employee.hasOwnProperty("role")); // true
console.log(employee.hasOwnProperty("name")); // false

Constructor Functions​

// Constructor function (pre-ES6 way)
function Person(name, age) {
this.name = name;
this.age = age;
}

// Methods on prototype (shared by all instances)
Person.prototype.greet = function () {
return `Hi, I'm ${this.name}`;
};

Person.prototype.isAdult = function () {
return this.age >= 18;
};

const john = new Person("John", 30);
const jane = new Person("Jane", 25);

console.log(john.greet()); // "Hi, I'm John"
console.log(jane.greet()); // "Hi, I'm Jane"

// Both share the same greet function
console.log(john.greet === jane.greet); // true

ES6 Classes (Syntactic Sugar)​

// Classes are just syntactic sugar over prototypes
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}

greet() {
return `Hi, I'm ${this.name}`;
}

static createAnonymous() {
return new Person("Anonymous", 0);
}
}

class Employee extends Person {
constructor(name, age, role) {
super(name, age);
this.role = role;
}

greet() {
return `${super.greet()}, I work as a ${this.role}`;
}
}

const dev = new Employee("John", 30, "Developer");
console.log(dev.greet()); // "Hi, I'm John, I work as a Developer"

Prototype vs proto vs prototype​

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

const john = new Person("John");

// prototype: Property of constructor functions
console.log(Person.prototype); // { constructor: Person }

// __proto__: Reference to object's prototype (deprecated, use Object.getPrototypeOf)
console.log(john.__proto__ === Person.prototype); // true
console.log(Object.getPrototypeOf(john) === Person.prototype); // true

// All instances share the same prototype
const jane = new Person("Jane");
console.log(john.__proto__ === jane.__proto__); // true

Property Lookup Chain​

const animal = {
eats: true,
walk() {
return "Walking...";
},
};

const rabbit = Object.create(animal);
rabbit.jumps = true;

rabbit.walk = function () {
return "Hopping!"; // Overrides parent
};

console.log(rabbit.jumps); // true (own property)
console.log(rabbit.eats); // true (inherited)
console.log(rabbit.walk()); // "Hopping!" (own method shadows parent)

Object.create() Patterns​

// Create object with specific prototype
const personMethods = {
greet() {
return `Hello, ${this.name}`;
},
introduce() {
return `I am ${this.name}, ${this.age} years old`;
},
};

function createPerson(name, age) {
const person = Object.create(personMethods);
person.name = name;
person.age = age;
return person;
}

const john = createPerson("John", 30);
console.log(john.greet()); // "Hello, John"

Checking Prototypes​

const arr = [1, 2, 3];

// instanceof checks prototype chain
console.log(arr instanceof Array); // true
console.log(arr instanceof Object); // true

// isPrototypeOf checks if object is in chain
console.log(Array.prototype.isPrototypeOf(arr)); // true
console.log(Object.prototype.isPrototypeOf(arr)); // true

// Get and set prototype
console.log(Object.getPrototypeOf(arr) === Array.prototype); // true

// Set prototype (not recommended for performance)
const proto = { greet: () => "Hi" };
const obj = {};
Object.setPrototypeOf(obj, proto);

Comparison with Classical Inheritance​

Classical (Java/C++)Prototypal (JavaScript)
Classes define blueprintsObjects inherit from objects
Copy of class creates objectObjects link to prototype
Rigid hierarchyFlexible, dynamic
Methods copied to each instanceMethods shared on prototype

Performance Note​

// ✅ Methods on prototype (shared, memory efficient)
function Person(name) {
this.name = name;
}
Person.prototype.greet = function () {
return `Hi, ${this.name}`;
};

// ❌ Methods in constructor (copied to each instance)
function Person(name) {
this.name = name;
this.greet = function () {
// New function per instance!
return `Hi, ${this.name}`;
};
}

Key Points​

  • Objects inherit directly from other objects
  • Prototype chain is followed until null
  • Properties on object shadow prototype properties
  • Methods should be on prototype for efficiency
  • ES6 classes are syntactic sugar over prototypes
  • Use Object.getPrototypeOf() instead of __proto__