Skip to article
ALGORITHMICSPatterns
Patterns5 min read

Prototype

Copying an existing object instead of constructing one — and the depth of the copy.


Sometimes constructing an object is expensive, or you do not know its class, or the state you want already exists somewhere. In all three cases the answer is the same: copy the one you have.

interface Prototype<T> {
clone(): T;
}
class Enemy implements Prototype<Enemy> {
constructor(
private readonly stats: Stats,
private readonly loadout: Weapon[],
) {}
clone(): Enemy {
// Deep where it must be: two enemies must not share one weapon list.
return new Enemy({...this.stats}, this.loadout.map((w) => w.clone()));
}
}

Spawn a wave by cloning a configured template, rather than re-reading the config, re-loading the model, and re-running the setup for each one.

Shallow or deep is the entire question

const shallow = {...original}; // nested objects are SHARED
const deep = structuredClone(original); // nested objects are copied

Clone, then damage the copy, and watch what happens to the template:

template

100 hp

copy

Press clone(), then damage the copy.

structuredClone is the built-in deep copy, and it handles cycles, Map, Set, Date and typed arrays. It does not copy functions, DOM nodes, or class identity — a cloned instance comes back as a plain object, which is usually not what you wanted for a class-based prototype. That is precisely why an explicit clone() method still earns its place.

When it is worth it

Construction is expensive. Parsing, network fetches, texture loading. Configure once, clone many.

The class is unknown. You have a Shape and want another like it. Cloning does not require knowing whether it is a circle — this is Prototype’s original motivation and the reason it is a creational pattern at all.

The state you want is the state you have. Duplicating a document, forking a config, “save as”.

In JavaScript, note the name collision

JavaScript’s prototype — the object every object delegates lookups to — is a different thing that happens to share the word. Object.create(x) makes an object that delegates to x; it does not copy it, so later changes to x are visible through the new object.

That is delegation, not cloning, and confusing the two produces objects that mysteriously change together.