JavaScript
07 / 07

Prototypes & Inheritance

JavaScript Prototypes & Inheritance

JavaScript uses prototypal inheritance. Every object has a prototype object from which it inherits properties and methods.

Prototype Chain

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

// Add method to prototype
Person.prototype.greet = function() {
  console.log(`Hello, I'm ${this.name}`);
};

Person.prototype.getAge = function() {
  return this.age;
};

const john = new Person('John', 30);
john.greet();  // 'Hello, I'm John'

// Prototype chain lookup
console.log(john.name);           // Own property
console.log(john.greet);          // From Person.prototype
console.log(john.toString);       // From Object.prototype

// Check prototype
console.log(john.__proto__ === Person.prototype);  // true
console.log(Person.prototype.__proto__ === Object.prototype);  // true
console.log(Object.prototype.__proto__);  // null (end of chain)

// hasOwnProperty
console.log(john.hasOwnProperty('name'));   // true
console.log(john.hasOwnProperty('greet'));  // false (in prototype)

// instanceof
console.log(john instanceof Person);  // true
console.log(john instanceof Object);  // true

Prototypal Inheritance

// Parent constructor
function Animal(name) {
  this.name = name;
}

Animal.prototype.eat = function() {
  console.log(`${this.name} is eating`);
};

// Child constructor
function Dog(name, breed) {
  Animal.call(this, name);  // Call parent constructor
  this.breed = breed;
}

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

// Add child methods
Dog.prototype.bark = function() {
  console.log(`${this.name} says woof!`);
};

const dog = new Dog('Buddy', 'Golden Retriever');
dog.eat();   // 'Buddy is eating' (inherited)
dog.bark();  // 'Buddy says woof!' (own)

console.log(dog instanceof Dog);     // true
console.log(dog instanceof Animal);  // true

// ES6 Classes (syntactic sugar over prototypes)
class AnimalES6 {
  constructor(name) {
    this.name = name;
  }
  
  eat() {
    console.log(`${this.name} is eating`);
  }
}

class DogES6 extends AnimalES6 {
  constructor(name, breed) {
    super(name);
    this.breed = breed;
  }
  
  bark() {
    console.log(`${this.name} says woof!`);
  }
}

Object.create() and Object.setPrototypeOf()

// Object.create() - creates object with specific prototype
const personPrototype = {
  greet() {
    console.log(`Hello, I'm ${this.name}`);
  }
};

const john = Object.create(personPrototype);
john.name = 'John';
john.greet();  // 'Hello, I'm John'

// Object.create with properties
const jane = Object.create(personPrototype, {
  name: {
    value: 'Jane',
    writable: true,
    enumerable: true
  },
  age: {
    value: 28,
    writable: true
  }
});

// Object.setPrototypeOf() - change prototype (avoid in production)
const dog = { name: 'Buddy' };
Object.setPrototypeOf(dog, personPrototype);
dog.greet();  // 'Hello, I'm Buddy'

// Object.getPrototypeOf()
console.log(Object.getPrototypeOf(john) === personPrototype);  // true

Keep your own version of these notes — editable, searchable, and organised by your stack.

Start free