JavaScript Prototypes and Inheritance
Understand JavaScript's prototype-based inheritance system. Learn how objects inherit properties and methods, and how to work with prototypes.
Topics Covered:
Prerequisites:
- JavaScript Arrays and Objects
Video Tutorial
Overview
JavaScript uses prototype-based inheritance, not class-based inheritance like many other languages. Understanding prototypes is crucial for understanding how JavaScript objects work, how inheritance functions, and how ES6 classes work under the hood. This tutorial covers the prototype chain, how inheritance works, Object.create, constructor functions, and how ES6 classes relate to prototypes.
Understanding Prototypes
Every JavaScript object has a prototype. The prototype is another object that the current object inherits properties and methods from. Prototype Basics: • Every object has __proto__ property • Points to prototype object • Prototype chain allows inheritance • Object.prototype is root of chain Accessing Prototypes: • obj.__proto__ (deprecated, but works) • Object.getPrototypeOf(obj) (preferred) • Object.setPrototypeOf(obj, proto) (set prototype)
// Every object has a prototype
const obj = {};
console.log(obj.__proto__); // Points to Object.prototype
console.log(Object.getPrototypeOf(obj)); // Same, preferred method
// Prototype chain
const arr = [1, 2, 3];
console.log(arr.__proto__); // Array.prototype
console.log(arr.__proto__.__proto__); // Object.prototype
console.log(arr.__proto__.__proto__.__proto__); // null (end of chain)
// Inheritance through prototype chain
const person = {
name: "Alice",
greet: function() {
return `Hello, I'm ${this.name}`;
}
};
const student = Object.create(person);
student.name = "Bob";
student.study = function() {
return "Studying...";
};
console.log(student.greet()); // "Hello, I'm Bob" - inherited!
console.log(student.study()); // "Studying..." - own method
// How JavaScript finds properties
// 1. Check object itself
// 2. Check prototype
// 3. Check prototype's prototype
// 4. Continue up chain until nullPrototypes enable inheritance in JavaScript. Objects inherit from their prototype through the prototype chain. JavaScript looks up properties through this chain.
Constructor Functions
Constructor functions are used to create objects with shared properties and methods. They're the traditional way to implement inheritance. Constructor Functions: • Functions used with new keyword • Create objects with shared prototype • this refers to new object • Convention: Capitalize constructor name Prototype Property: • Constructor functions have .prototype property • Objects created inherit from this prototype • Add methods to prototype for efficiency
// Constructor function
function Person(name, age) {
this.name = name;
this.age = age;
}
// Add methods to prototype (shared by all instances)
Person.prototype.greet = function() {
return `Hello, I'm ${this.name}`;
};
Person.prototype.getAge = function() {
return this.age;
};
// Create instances
const person1 = new Person("Alice", 30);
const person2 = new Person("Bob", 25);
console.log(person1.greet()); // "Hello, I'm Alice"
console.log(person2.greet()); // "Hello, I'm Bob"
// Both share same prototype
console.log(person1.__proto__ === Person.prototype); // true
console.log(person2.__proto__ === Person.prototype); // true
// Inheritance with constructor functions
function Student(name, age, school) {
Person.call(this, name, age); // Call parent constructor
this.school = school;
}
// Set up prototype chain
Student.prototype = Object.create(Person.prototype);
Student.prototype.constructor = Student;
Student.prototype.study = function() {
return "Studying...";
};
const student = new Student("Charlie", 20, "University");
console.log(student.greet()); // Inherited from Person
console.log(student.study()); // Own methodConstructor functions create objects with shared prototypes. Add methods to prototype for efficiency. Use Object.create() for inheritance.
ES6 Classes and Prototypes
ES6 classes are syntactic sugar over constructor functions and prototypes. They make inheritance easier but work the same way under the hood. ES6 Classes: • class keyword • constructor method • Methods on prototype • extends for inheritance • super for parent access Under the Hood: • Classes are constructor functions • Methods go on prototype • extends sets up prototype chain • Works exactly like constructor functions
// ES6 Class (syntactic sugar)
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
greet() {
return `Hello, I'm ${this.name}`;
}
getAge() {
return this.age;
}
}
// Under the hood, this is equivalent to:
// function Person(name, age) {
// this.name = name;
// this.age = age;
// }
// Person.prototype.greet = function() { ... };
const person = new Person("Alice", 30);
console.log(person.greet()); // "Hello, I'm Alice"
// Inheritance with classes
class Student extends Person {
constructor(name, age, school) {
super(name, age); // Call parent constructor
this.school = school;
}
study() {
return "Studying...";
}
}
const student = new Student("Bob", 20, "University");
console.log(student.greet()); // Inherited
console.log(student.study()); // Own method
// Static methods
class MathUtils {
static add(a, b) {
return a + b;
}
}
console.log(MathUtils.add(5, 3)); // 8
// Called on class, not instanceES6 classes are syntactic sugar over prototypes. They make inheritance easier but work the same way. extends sets up the prototype chain automatically.
Object.create() for Prototype Inheritance
Object.create() is a clean way to create objects with a specific prototype. It's useful for creating inheritance without constructor functions. Object.create(): • Creates object with specified prototype • More explicit than constructor functions • Clean inheritance pattern • Used in modern JavaScript
// Object.create() for inheritance
const person = {
name: "Unknown",
greet() {
return `Hello, I'm ${this.name}`;
}
};
// Create object with person as prototype
const alice = Object.create(person);
alice.name = "Alice";
console.log(alice.greet()); // "Hello, I'm Alice"
// Factory function with Object.create
function createPerson(name, age) {
const person = Object.create(personMethods);
person.name = name;
person.age = age;
return person;
}
const personMethods = {
greet() {
return `Hello, I'm ${this.name}`;
},
getAge() {
return this.age;
}
};
const bob = createPerson("Bob", 25);
console.log(bob.greet()); // "Hello, I'm Bob"
// Multiple levels of inheritance
const animal = {
eat() {
return "Eating...";
}
};
const dog = Object.create(animal);
dog.bark = function() {
return "Woof!";
};
const myDog = Object.create(dog);
myDog.name = "Buddy";
console.log(myDog.eat()); // Inherited from animal
console.log(myDog.bark()); // Inherited from dogObject.create() creates objects with specific prototypes. It's a clean way to implement inheritance without constructor functions. Useful for modern JavaScript patterns.
Conclusion
Prototypes are fundamental to JavaScript. You've learned how the prototype chain works, constructor functions, ES6 classes (which are syntactic sugar), and Object.create(). Understanding prototypes helps you understand how JavaScript inheritance works, how classes work under the hood, and how to create efficient object hierarchies. This knowledge is valuable even in React, as it helps you understand JavaScript's object model.