In JavaScript, every function has a prototype property, and every object (except null) is linked to another object known as its prototype. This linkage forms the basis of property inheritance through delegation rather than copying.
function Person() {}
Person.prototype.name = 'Kevin';
const person1 = new Person();
const person2 = new Person();
console.log(person1.name); // Kevin
console.log(person2.name); // Kevin
The prototype of a constructor function can be accessed via Constructor.prototype. Instances created from that constructor link to this prototype through their internal [[Prototype]], exposed in most environments as __proto__:
function Person() {}
const person = new Person();
console.log(person.__proto__ === Person.prototype); // true
Each prototype object also holds a constructor property pointing back to its associated constructor:
function Person() {}
console.log(Person === Person.prototype.constructor); // true
There are three standard ways to access an object’s prototype:
Constructor.prototypeinstance.__proto__Object.getPrototypeOf(instance)
When accessing a property, JavaScript first checks the object itself. If the property isn’t found, it traverses up the prototype chain until it either finds the property or reaches the end (null), returning undefined if not found:
function Person() {}
Person.prototype.name = 'Kevin';
const person = new Person();
person.name = 'Daisy';
console.log(person.name); // Daisy
delete person.name;
console.log(person.name); // Kevin
At the top of the prototype chain lies Object.prototype, whose __proto__ is null:
console.log(Object.prototype.__proto__ === null); // true
JavaScript uses delegation: objects access properties from their prototypes without copying them.
Prototype Inspection Methods
Object.getPrototypeOf(obj) returns the prototype of obj:
function F() {}
const f = new F();
console.log(Object.getPrototypeOf(f) === F.prototype); // true
console.log(Object.getPrototypeOf(f) === f.__proto__); // true
prototype.isPrototypeOf(obj) checks if the prototype exists in obj’s chain:
function Demo() {}
const demo = new Demo();
console.log(Demo.prototype.isPrototypeOf(demo)); // true
console.log(Object.prototype.isPrototypeOf(demo)); // true
instanceof tests whether a constructor’s prototype appears in the instance’s prototype chain:
function Demo() {}
const demo = new Demo();
console.log(demo instanceof Demo); // true
console.log(demo instanceof Object); // true
console.log(Function instanceof Object); // true
console.log(Object instanceof Function); // true
Note: instanceof does not check how an object was created—it checks prototype chain membership. Changing a constructor’s prototype after creating an instance breaks the instanceof relationship:
function Person() {}
const p1 = new Person();
console.log(p1 instanceof Person); // true
Person.prototype = { constructor: Person };
console.log(p1 instanceof Person); // false
const p2 = new Person();
console.log(p2 instanceof Person); // true
Inheritance Patterns
Prototype-based inheritance directly assigns one prototype to another:
function Super() {}
Super.prototype.info = 'Super info';
function Sub() {}
Sub.prototype = Super.prototype;
Sub.prototype.constructor = Sub;
const s = new Sub();
console.log(s.info); // Super info
Prototype chain inheritance sets the parent’s instance as the child’s prototype:
function Super() {
this.name = 'Super name';
}
Super.prototype.info = 'Super info';
function Sub() {}
Sub.prototype = new Super();
Sub.prototype.constructor = Sub;
const s = new Sub();
console.log(s.name); // Super name
console.log(s.info); // Super info
This approach doesn’t support passing arguemnts to the parent constructor during child instantiation.
Combination inheritance merges constructor stealing with prototype chaining to inherit both instance and prototype properties while enabling parameter passing:
function Super(name) {
this.name = name;
}
Super.prototype.info = 'Super info';
function Sub(name) {
Super.call(this, name); // inherit instance properties
}
Sub.prototype = Object.create(Super.prototype);
Sub.prototype.constructor = Sub;
const s1 = new Sub('Alice');
const s2 = new Sub('Bob');
console.log(s1.name, s2.name); // Alice Bob
console.log(s1.info); // Super info