Design Patterns: Prototype
Prototype creates new objects by cloning an existing one. Instead of going through the full initialization process again, you start from an existing instance and adjust from there.
Fits best when: object initialization is expensive, and new objects are mostly slight variations of existing ones.
Intent
Core idea: copy an existing object, then make small adjustments to the copy.
Common scenarios:
- Duplicating graphic layers in a design tool
- Cloning object templates in a game engine (bullets, NPC types)
- Settings pages with presets that users customize
Shallow vs. Deep Copy
This is the most important part of working with Prototype.
Shallow copy: copies only the top-level properties. Nested objects and arrays are still shared between the original and the clone.
const original = { a: 1, b: { c: 2 } };
const shallow = { ...original };
shallow.b.c = 99;
console.log(original.b.c); // 99 — both point to the same object!Deep copy: copies everything, including nested objects. The two instances are fully independent.
const deep = JSON.parse(JSON.stringify(original));
deep.b.c = 99;
console.log(original.b.c); // 2 — safeJSON.parse(JSON.stringify(...)) is simple but limited — it doesn't handle undefined, Date, functions, or circular references. For complex objects, a proper clone() method is the right approach.
Practical Example: Graphics Editor
interface Cloneable {
clone(): this;
}
class Shape implements Cloneable {
constructor(
public type: string,
public x: number,
public y: number,
public style: { color: string; strokeWidth: number },
) {}
// deep clone — style object is copied too
clone(): this {
return Object.assign(Object.create(Object.getPrototypeOf(this)), {
...this,
style: { ...this.style },
});
}
moveTo(x: number, y: number): this {
this.x = x;
this.y = y;
return this;
}
}
const original = new Shape('circle', 10, 20, { color: 'red', strokeWidth: 2 });
// clone and move to a new position
const cloned = original.clone().moveTo(50, 60);
console.log(original.x, original.y); // 10, 20 — unchanged
cloned.style.color = 'blue';
console.log(original.style.color); // 'red' — safe, independent copyWhen to Use
Good fits
- Object initialization is expensive or resource-intensive, and new objects are mostly variations
- You need to snapshot an object's state and branch from that point in the future
Watch out for
- Objects with internal references require a decision: shallow or deep?
- The
clone()method must account for all internal state, including inherited properties
Summary
Prototype lets you create a new object without knowing its exact class — just clone an existing instance. It's commonly used for preset configurations, object templates, state snapshots, and object pool initialization.
Shallow copy is always the default. If an object has reference-type properties, implement clone() explicitly.