Prototype Pattern
CreationalWhat is it?
Creates objects based on a template of an existing object through cloning. JavaScript's prototypal inheritance makes this pattern very natural.
Why use it?
The Prototype pattern is used to create new objects by copying an existing object, known as the prototype. This is useful when the cost of creating an object is more expensive or complex than copying an existing one. It leverages JavaScript's prototypal inheritance and allows for creating objects dynamically.
Code Example
typescript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
// Prototype Pattern Example
class ShapePrototype {
constructor() {
this.x = 0;
this.y = 0;
this.color = '#000000';
}
clone() {
// Create a new instance with the same prototype
const cloned = Object.create(Object.getPrototypeOf(this));
// Copy all properties
for (let prop in this) {
if (this.hasOwnProperty(prop)) {
// Deep clone objects and arrays
if (typeof this[prop] === 'object' && this[prop] !== null) {
cloned[prop] = Array.isArray(this[prop])
? [...this[prop]]
: { ...this[prop] };
} else {
cloned[prop] = this[prop];
}
}
}
return cloned;
}
move(x, y) {
this.x = x;
this.y = y;
}
setColor(color) {
this.color = color;
}
}
class Rectangle extends ShapePrototype {
constructor(width = 0, height = 0) {
super();
this.width = width;
this.height = height;
this.type = 'rectangle';
}
getArea() {
return this.width * this.height;
}
}
class Circle extends ShapePrototype {
constructor(radius = 0) {
super();
this.radius = radius;
this.type = 'circle';
}
getArea() {
return Math.PI * this.radius * this.radius;
}
}
// Shape Registry for managing prototypes
class ShapeRegistry {
constructor() {
this.shapes = {};
}
registerShape(name, shape) {
this.shapes[name] = shape;
}
createShape(name) {
const prototype = this.shapes[name];
if (!prototype) {
throw new Error(`Shape '${name}' not found in registry`);
}
return prototype.clone();
}
}
// Usage
const registry = new ShapeRegistry();
// Register prototype shapes
const rectanglePrototype = new Rectangle(100, 50);
rectanglePrototype.setColor('#FF0000');
registry.registerShape('red-rectangle', rectanglePrototype);
const circlePrototype = new Circle(30);
circlePrototype.setColor('#0000FF');
registry.registerShape('blue-circle', circlePrototype);
// Create new shapes from prototypes
const rect1 = registry.createShape('red-rectangle');
rect1.move(10, 20);
const rect2 = registry.createShape('red-rectangle');
rect2.move(100, 200);
rect2.setColor('#00FF00'); // Change color of this instance
const circle1 = registry.createShape('blue-circle');
circle1.move(50, 50);
console.log(rect1); // Rectangle at(10, 20) with red color
console.log(rect2); // Rectangle at(100, 200) with green color
console.log(circle1); // Circle at(50, 50) with blue color
// Native JavaScript prototype example
const carPrototype = {
wheels: 4,
start() {
console.log('Engine started');
},
clone() {
return Object.create(this);
}
};
const car1 = carPrototype.clone();
car1.brand = 'Toyota';
car1.model = 'Camry';
const car2 = carPrototype.clone();
car2.brand = 'Honda';
car2.model = 'Accord';
console.log(car1.wheels); // 4 (inherited from prototype)
console.log(car2.wheels); // 4 (inherited from prototype)
Quick Facts
- Category
- Creational
- Common Use Cases
- Object creation, instance management