Skip to content

Prototype

Prototype creates new objects by copying an existing, fully configured instance — the prototype — rather than constructing one from scratch.

Sometimes building an object from its constructor is expensive or awkward: it might require an expensive computation, a lot of configuration, or knowledge the caller does not have. If you already hold an instance that is almost what you want, copying it and tweaking the copy is simpler than rebuilding it. Prototype makes “give me one like this” a first-class operation: the object knows how to clone itself.

The decision that defines Prototype is shallow versus deep. A shallow copy duplicates the top-level object but shares its nested references, so mutating a nested field in the copy also changes the original. A deep copy duplicates the whole graph, so the copy is fully independent. Choosing wrong is a classic source of bugs — a “copy” that secretly shares a list with its source will surprise you the first time either side is mutated. Prototype forces you to make that choice on purpose.

classDiagram
  class Prototype {
    <<interface>>
    +clone() Prototype
  }
  class Document {
    +title: string
    +tags: List
    +clone() Document
  }
  class Client {
    +duplicate(p) Prototype
  }
  Prototype <|.. Document
  Client ..> Prototype : clones
A prototype exposes a clone operation the client calls
  • Prototype — the interface declaring a clone operation.
  • Concrete Prototype (Document) — implements clone to produce a copy of itself, deciding shallow vs. deep.
  • Client — obtains new objects by cloning a prototype instead of calling a constructor.

A Document with a list of tags. We clone it deeply so editing the copy’s tags leaves the original untouched.

class Document {
constructor(
public title: string,
public tags: string[],
) {}
// Deep clone: structuredClone copies the nested array too.
clone(): Document {
const copy = structuredClone({ title: this.title, tags: this.tags });
return new Document(copy.title, copy.tags);
}
}
const original = new Document('Report', ['draft', 'q2']);
const copy = original.clone();
copy.title = 'Report (revised)';
copy.tags.push('final');
console.log(original.title, original.tags); // Report ['draft', 'q2']
console.log(copy.title, copy.tags); // Report (revised) ['draft', 'q2', 'final']
  • Pro: cheaper than rebuilding when an object is expensive or complicated to construct.
  • Pro: the client copies an existing object without depending on its concrete class or constructor arguments.
  • Pro: lets you keep a library of preconfigured prototypes and stamp out variations.
  • Con: cloning object graphs with cycles or shared resources (open files, sockets) is tricky.
  • Con: the shallow-versus-deep distinction is easy to get wrong, producing copies that secretly share state.
  • Builder constructs an object step by step, while Prototype starts from a ready-made one.
  • Abstract Factory can store and return prototypes instead of constructing each product fresh.
How does Prototype create a new object?
What is the difference between a shallow and a deep copy?
Why can a shallow copy cause bugs?