Prototypes and Prototypal Inheritance in JavaScript
Prototypal inheritance is the foundation of the object model in JavaScript. Unlike classical languages (Java, C++), which use classical inheritance, JavaScript implements a dynamic model where objects can inherit properties and methods from each other through prototypes.
What is a Prototype?
A prototype is an object that any other object refers to if it doesn't have the property or method being searched for. Every object in JavaScript can have another object assigned as its prototype. If a property doesn't exist on the object itself during access, the interpreter will "climb up" the prototype chain until it finds the needed property or reaches the null object.
- childObject: object where property search is performed.
- parentObject: prototype of childObject.
- Object.prototype: base prototype from which all objects inherit unless specified otherwise.
- null: end of chain; if property not found by this point, result will be undefined.
[[Prototype]] Property and proto
In ECMAScript specification, every object has an internal [[Prototype]] property pointing to the prototype. Historically, many environments (e.g., browsers) provide getter/setter __proto__ for accessing it, but this is considered deprecated.
Modern way — Object.getPrototypeOf and Object.setPrototypeOf
const parent = { greet: function() { console.log("Hello!"); } };
const child = {};
// Set parent as child's prototype
Object.setPrototypeOf(child, parent);
// Now child has access to greet() through prototype chain
child.greet(); // "Hello!"
console.log(Object.getPrototypeOf(child) === parent); // true
Object.create
The most convenient way to create an object with needed prototype is using Object.create method
const person = {
sayName() {
console.log(`My name is ${this.name}`);
},
};
const john = Object.create(person);
john.name = "John";
john.sayName(); // My name is John
- person acts as "parent" (prototype).
- john — new object whose [[Prototype]] points to person.
Prototype Chain
If we call property/method john.sayName(), and john doesn't have such property, JS will search for this property in person. If person didn't have sayName, interpreter would go to person.__proto__, which is usually Object.prototype, and so on.
Constructor Functions and prototype Property
Before classes appeared in ES6 (ES2015), constructor functions were often used to simulate class behavior.
function User(name) {
this.name = name;
}
User.prototype.sayHi = function () {
console.log(`Hi, I'm ${this.name}`);
};
const alice = new User("Alice");
const bob = new User("Bob");
alice.sayHi(); // "Hi, I'm Alice"
bob.sayHi(); // "Hi, I'm Bob"
Useris a constructor function.- Property
User.prototypeis automatically created when function is declared, and it becomes prototype for all objects created withnew User(...). aliceandbobstore reference to instance property name, and methods are taken from User.prototype.
Classes and Prototypal Inheritance
In modern JS versions you can use class, which under the hood still works on prototypes:
class Person {
constructor(name) {
this.name = name;
}
greet() {
console.log(`Hello, I'm ${this.name}`);
}
}
const tom = new Person("Tom");
tom.greet(); // "Hello, I'm Tom"
// Check prototype
console.log(Object.getPrototypeOf(tom) === Person.prototype); // true
- Compiler creates constructor function
Personand setsPerson.prototypeproperty. All methods defined insideclass Person {}are written toPerson.prototype.
Summary
- Prototype is a mechanism allowing objects to inherit properties and methods from other objects.
- In JS every object has hidden [[Prototype]] property, usually referencing another object or null.
- Prototype chain allows searching for property "bottom-up": from object to its prototype, then to prototype's prototype, and so on.
- Constructor functions (before ES6) and classes (from ES6) use prototypes "under the hood". Classes are just "syntactic sugar" over constructor functions.
- Object.create — convenient way to create objects specifying prototype directly. Prototypal inheritance provides powerful, flexible approach to OOP implementation without strict binding to classes.