Last updated: Apr 29, 2026
Prototype
The Prototype pattern lets objects share behavior through a linked prototype object instead of duplicating it on every instance. In JavaScript this is built into the language — every object has an internal [[Prototype]] link that the engine follows when a property isn’t found on the object itself.
How It Works
Consider a factory function that creates many similar objects:
const createDog = (name: string, age: number) => ({
name,
age,
bark() {
console.log(`${name} is barking!`);
},
wagTail() {
console.log(`${name} is wagging their tail!`);
},
});
const dog1 = createDog("Max", 4);
const dog2 = createDog("Sam", 2);
Every call produces a brand-new copy of bark and wagTail. With four dogs you have eight function objects in memory doing the same thing.
The Prototype pattern avoids this by placing shared methods on a single prototype object. All instances delegate to it instead of owning their own copies.
Using a Class
ES6 classes wire up the prototype chain automatically. Methods defined in the class body live on ClassName.prototype and are shared across all instances.
class Dog {
constructor(
public name: string,
public age: number,
) {}
bark() {
console.log(`${this.name} is barking!`);
}
wagTail() {
console.log(`${this.name} is wagging their tail!`);
}
}
const dog1 = new Dog("Max", 4);
const dog2 = new Dog("Sam", 2);
dog1.bark(); // "Max is barking!"
dog2.wagTail(); // "Sam is wagging their tail!"
dog1 and dog2 don’t carry their own bark or wagTail. When you call dog1.bark(), the engine doesn’t find bark on dog1 itself, walks up to Dog.prototype, and finds it there.
Using Object.create
You can also set up a prototype chain without classes using Object.create, which creates a new object with the specified object as its prototype:
const dogPrototype = {
bark(this: { name: string }) {
console.log(`${this.name} is barking!`);
},
wagTail(this: { name: string }) {
console.log(`${this.name} is wagging their tail!`);
},
};
const dog1 = Object.create(dogPrototype);
dog1.name = "Max";
dog1.age = 4;
dog1.bark(); // "Max is barking!"
Object.create(dogPrototype) returns an empty object whose [[Prototype]] points to dogPrototype. Property lookups that fail on dog1 fall through to the prototype.
The Prototype Chain
Prototypes can themselves have prototypes, forming a chain. The engine walks the chain until it finds the property or reaches null.
class Animal {
constructor(public name: string) {}
eat() {
console.log(`${this.name} is eating`);
}
}
class Dog extends Animal {
bark() {
console.log(`${this.name} is barking!`);
}
}
class BorderCollie extends Dog {
herd() {
console.log(`${this.name} is herding`);
}
}
const rex = new BorderCollie("Rex");
rex.herd(); // found on BorderCollie.prototype
rex.bark(); // found on Dog.prototype
rex.eat(); // found on Animal.prototype
Trade-Offs
| Advantage | Disadvantage |
|---|---|
| Memory efficient — methods are stored once, not per instance | Deep prototype chains can make it hard to trace where a property comes from |
| Easy to add or change shared behavior in one place | Modifying a prototype at runtime affects all existing instances |
| Built into JavaScript — no extra library needed | this binding surprises when methods are passed as callbacks |
When to Use It
- You’re creating many objects that share the same methods and want to avoid duplicating functions in memory.
- You want a lightweight inheritance hierarchy where child types extend a base with additional behavior.
- You need to add shared behavior after object creation (by attaching methods to the prototype).