README
7.5 Prototypes
Table of Contents
- β’Introduction
- β’What is a Prototype?
- β’The Prototype Chain
- β’Accessing Prototypes
- β’Setting Prototypes
- β’Prototype Inheritance
- β’Constructor Functions and Prototypes
- β’The
prototypeProperty - β’Shadowing Properties
- β’Best Practices
Introduction
Prototypes are JavaScript's mechanism for inheritance. Every object in JavaScript has a hidden internal property called [[Prototype]] that references another object.
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β JavaScript Prototype System β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β Every object has a [[Prototype]] β
β βββββββββββββββ β
β β myObj β β
β β βββββββββ β β
β β name: "X" ββββββββΊ [[Prototype]] β
β βββββββββββββββ β β
β βΌ β
β βββββββββββββββ β
β βObject.proto β β
β β βββββββββ β β
β β toString() β β
β β valueOf() β β
β βββββββββββββββ β
β β β
β βΌ β
β null β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
What is a Prototype?
A prototype is an object from which other objects inherit properties. When you try to access a property on an object:
- β’JavaScript first looks for the property on the object itself
- β’If not found, it looks on the object's prototype
- β’This continues up the prototype chain until found or reaching
null
const animal = {
eats: true,
walk() {
console.log('Walking...');
},
};
const rabbit = {
jumps: true,
};
// Set animal as rabbit's prototype
Object.setPrototypeOf(rabbit, animal);
console.log(rabbit.jumps); // true (own property)
console.log(rabbit.eats); // true (inherited from animal)
rabbit.walk(); // "Walking..." (inherited method)
The Prototype Chain
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Prototype Chain Example β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β rabbit animal Object.prototype β
β ββββββββββββ ββββββββββββ ββββββββββββββββ β
β β jumps: β proto β eats: β proto β toString() β β
β β true βββββββββββΊ β true βββββββββΊ β valueOf() β β
β β β β walk() β β hasOwnProp() β β
β ββββββββββββ ββββββββββββ ββββββββββββββββ β
β β β
β βΌ β
β null β
β β
β Property Lookup for rabbit.toString(): β
β 1. Not in rabbit βββΊ 2. Not in animal βββΊ 3. Found in Object.protoβ
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
const rabbit = { jumps: true };
const animal = { eats: true };
Object.setPrototypeOf(rabbit, animal);
// Prototype chain lookup
console.log(rabbit.hasOwnProperty('jumps')); // true
console.log(rabbit.hasOwnProperty('eats')); // false (inherited)
// Check the chain
console.log(Object.getPrototypeOf(rabbit) === animal); // true
console.log(Object.getPrototypeOf(animal) === Object.prototype); // true
console.log(Object.getPrototypeOf(Object.prototype)); // null
Accessing Prototypes
Object.getPrototypeOf()
The recommended way to get an object's prototype:
const arr = [1, 2, 3];
const proto = Object.getPrototypeOf(arr);
console.log(proto === Array.prototype); // true
proto (Deprecated)
The legacy way - avoid in new code:
const obj = {};
console.log(obj.__proto__ === Object.prototype); // true
// Works but deprecated
obj.__proto__ = { custom: true };
isPrototypeOf()
Check if an object is in another's prototype chain:
const animal = { eats: true };
const rabbit = Object.create(animal);
console.log(animal.isPrototypeOf(rabbit)); // true
console.log(Object.prototype.isPrototypeOf(rabbit)); // true
Setting Prototypes
Object.create()
Create object with specific prototype (recommended):
const personProto = {
greet() {
return `Hello, I'm ${this.name}`;
},
get info() {
return `${this.name}, ${this.age} years old`;
},
};
const john = Object.create(personProto);
john.name = 'John';
john.age = 30;
console.log(john.greet()); // "Hello, I'm John"
console.log(john.info); // "John, 30 years old"
Object.setPrototypeOf()
Change an existing object's prototype (use sparingly):
const dog = { barks: true };
const animal = { eats: true };
Object.setPrototypeOf(dog, animal);
console.log(dog.eats); // true
// β οΈ Warning: This is slow - avoid if possible
Object.create(null)
Create object with no prototype:
const pureDict = Object.create(null);
pureDict.key = 'value';
console.log(pureDict.toString); // undefined (no Object.prototype)
console.log('key' in pureDict); // true
// Useful for dictionaries to avoid prototype pollution
Prototype Inheritance
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Prototype Inheritance Pattern β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β // Base prototype β
β const vehicle = { β
β start() { console.log("Starting..."); }, β
β stop() { console.log("Stopping..."); } β
β }; β
β β
β // Child prototype inherits from vehicle β
β const car = Object.create(vehicle); β
β car.honk = function() { console.log("Beep!"); }; β
β β
β // Instance inherits from car (and vehicle) β
β const myCar = Object.create(car); β
β myCar.make = "Toyota"; β
β β
β myCar.start(); // "Starting..." (from vehicle) β
β myCar.honk(); // "Beep!" (from car) β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Example: Multi-Level Inheritance
// Level 1: Base
const living = {
breathe() {
console.log(`${this.name} is breathing`);
},
};
// Level 2: Inherits from living
const animal = Object.create(living);
animal.eat = function () {
console.log(`${this.name} is eating`);
};
// Level 3: Inherits from animal
const dog = Object.create(animal);
dog.bark = function () {
console.log(`${this.name} says woof!`);
};
// Instance
const rex = Object.create(dog);
rex.name = 'Rex';
rex.breathe(); // "Rex is breathing"
rex.eat(); // "Rex is eating"
rex.bark(); // "Rex says woof!"
Constructor Functions and Prototypes
When you create objects with new, the constructor's prototype property becomes the new object's [[Prototype]].
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Constructor Function and prototype Property β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β function Person(name) { Person.prototype β
β this.name = name; βββββββββββββββββββ β
β } β constructor: β β
β ββββββββββββΊβ Person β β
β Person.prototype βββββ β greet: fn() β β
β βββββββββββββββββββ β
β β² β
β const john = new Person("John") β [[Prototype]] β
β ββββββββββββββββ β β
β β name: "John" ββββββββββββββββββββββββββ β
β ββββββββββββββββ β
β β
β john.greet() β looks in john β not found β looks in prototype β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
function Person(name, age) {
this.name = name;
this.age = age;
}
// Add methods to prototype (shared by all instances)
Person.prototype.greet = function () {
return `Hi, I'm ${this.name}`;
};
Person.prototype.birthday = function () {
this.age++;
return `Happy birthday! Now ${this.age}`;
};
const alice = new Person('Alice', 30);
const bob = new Person('Bob', 25);
console.log(alice.greet()); // "Hi, I'm Alice"
console.log(bob.greet()); // "Hi, I'm Bob"
// Both share the same prototype methods
console.log(alice.greet === bob.greet); // true
console.log(Object.getPrototypeOf(alice) === Person.prototype); // true
The prototype Property
Only functions have a prototype property. This is NOT the function's own prototype - it's the prototype that will be assigned to objects created with new.
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β prototype vs [[Prototype]] β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β Function.prototype β Function's OWN prototype (for inheritance)β
β β
β MyFunction.prototype β Template for objects created with `new` β
β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β β β
β β function Dog(name) { β β
β β this.name = name; β β
β β } β β
β β β β
β β Dog.prototype.bark = function() { ... } β β
β β β β β
β β This is what new Dog() instances will inherit from β β
β β β β
β β Object.getPrototypeOf(Dog) === Function.prototype β β
β β β β β
β β This is Dog's own prototype (it inherits from Function) β β
β β β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
function Dog(name) {
this.name = name;
}
// Dog.prototype - template for instances
Dog.prototype.bark = function () {
console.log('Woof!');
};
// Dog's own prototype - it's a function
console.log(Object.getPrototypeOf(Dog) === Function.prototype); // true
const rex = new Dog('Rex');
// rex's prototype is Dog.prototype
console.log(Object.getPrototypeOf(rex) === Dog.prototype); // true
// The constructor property
console.log(Dog.prototype.constructor === Dog); // true
console.log(rex.constructor === Dog); // true
Shadowing Properties
When an object has a property with the same name as one in its prototype, the object's property "shadows" the prototype's.
const parent = {
value: 10,
getValue() {
return this.value;
},
};
const child = Object.create(parent);
child.value = 20; // Shadows parent.value
console.log(parent.getValue()); // 10
console.log(child.getValue()); // 20 (uses child.value)
// Check where properties come from
console.log(child.hasOwnProperty('value')); // true
console.log(child.hasOwnProperty('getValue')); // false
// Delete shadows to reveal inherited
delete child.value;
console.log(child.getValue()); // 10 (now uses parent.value)
Shadowing with Getters/Setters
const parent = {
_value: 10,
get value() {
return this._value;
},
set value(v) {
this._value = v;
},
};
const child = Object.create(parent);
// This uses parent's setter, setting _value on child
child.value = 20;
console.log(child._value); // 20 (own property)
console.log(parent._value); // 10 (unchanged)
console.log(child.value); // 20
Common Prototype Patterns
Extending Built-in Prototypes (Use Carefully!)
// Adding to Array.prototype (generally discouraged)
if (!Array.prototype.last) {
Object.defineProperty(Array.prototype, 'last', {
get() {
return this[this.length - 1];
},
});
}
const arr = [1, 2, 3];
console.log(arr.last); // 3
Prototype-Based Mixin
const canWalk = {
walk() {
console.log(`${this.name} is walking`);
},
};
const canSwim = {
swim() {
console.log(`${this.name} is swimming`);
},
};
const canFly = {
fly() {
console.log(`${this.name} is flying`);
},
};
// Create a duck with multiple capabilities
function Duck(name) {
this.name = name;
}
Object.assign(Duck.prototype, canWalk, canSwim, canFly);
const donald = new Duck('Donald');
donald.walk(); // "Donald is walking"
donald.swim(); // "Donald is swimming"
donald.fly(); // "Donald is flying"
Best Practices
1. Use Object.create() for Prototypal Inheritance
// β Good
const child = Object.create(parent);
// β Avoid
child.__proto__ = parent;
2. Don't Modify Object.prototype
// β Very dangerous - affects ALL objects
Object.prototype.myMethod = function () {};
// β Create your own base object
const myBase = {
myMethod() {},
};
3. Use Object.hasOwn() to Check Own Properties
// β Good (ES2022+)
if (Object.hasOwn(obj, 'prop')) {
}
// β Also good (older approach)
if (Object.prototype.hasOwnProperty.call(obj, 'prop')) {
}
// β Can be overridden
if (obj.hasOwnProperty('prop')) {
}
4. Prefer Composition Over Inheritance
// β Good - Composition
const canFly = (obj) => ({
fly() {
console.log(`${obj.name} flies`);
},
});
const bird = { name: 'Eagle' };
Object.assign(bird, canFly(bird));
// Rather than deep inheritance chains
5. Use Classes for Constructor-Based Patterns
// β Modern - clearer syntax
class Animal {
constructor(name) {
this.name = name;
}
speak() {}
}
class Dog extends Animal {
bark() {}
}
Summary Table
| Concept | Description |
|---|---|
[[Prototype]] | Internal link to prototype object |
Object.getPrototypeOf() | Get an object's prototype |
Object.setPrototypeOf() | Set an object's prototype |
Object.create() | Create object with specific prototype |
Func.prototype | Template for objects created with new Func() |
instanceof | Check if object inherits from constructor |
isPrototypeOf() | Check if object is in prototype chain |
Files in This Section
- β’
README.md- This documentation - β’
examples.js- Runnable code examples - β’
exercises.js- Practice exercises with solutions
Navigation
- β’Previous: 7.4 Object Static Methods
- β’Next: 7.6 Object Patterns
- β’Back to Module 7