codejump

AcademyDesign Patterns

Prototype pattern

Cloning a configured instance instead of rebuilding from scratch, the per-field copy it costs, nested fields included, and a shallow clone() copying a reference, not a value.

6 min readUpdated

3 exercises

Waiting at the end of this page.

Prototype is for when building from scratch is the wrong move entirely, when copying an object you already have, configured, is cheaper than reconstructing one from raw parameters.

Some objects are expensive or awkward to construct from raw parameters. They were loaded from a file, tuned by hand over several calls, or assembled through a Builder already. Prototype skips reconstructing one of those from scratch: it clones an existing, already-configured instance instead, then lets the caller tweak the copy.

The problem it solves

You are building a game with hundreds of goblins. Every goblin has the same stats, the same loot table and the same behavior, loaded from a configuration file and validated on the way in.

If each new goblin is built from scratch, you reload and revalidate the same data hundreds of times. And if the setup involved a slow step, such as a network call, a database read or a heavy computation, you pay that price every time.

The same shape appears outside games:

  • A user clicks "duplicate" on a document, a challenge or a dashboard, and expects an independent copy.
  • A test needs a fresh copy of the same fixture, so one test's edits do not leak into the next.
  • An app keeps a snapshot of a form's state so it can undo changes.

The fix is to build one well-configured prototype and produce every other instance by copying it.

Structure

The client asks an existing instance to clone itself and gets an independent copy back
The client asks an existing instance to clone itself and gets an independent copy back

Two parts:

  • The prototype: an existing, configured object.
  • A `clone()` operation that returns a new, independent object with the same state. The class copies itself, so the caller never needs to know its fields or its concrete type.
class Enemy {
  constructor(kind, hp, loot) {
    this.kind = kind;
    this.hp = hp;
    this.loot = loot;
  }
  clone() {
    return new Enemy(this.kind, this.hp, [...this.loot]);
  }
}

const goblinTemplate = new Enemy('goblin', 20, ['dagger']);
const goblin2 = goblinTemplate.clone();
goblin2.hp = 25; // tweak the copy — the template stays untouched

The [...this.loot] (list(self.loot) in Python) is doing real work, not decoration. Copy the array reference instead of its contents and goblin2.loot.push(...) would silently mutate the template every future clone starts from. That is the shallow versus deep copy trap this pattern lives or dies by: a Prototype whose clone() shares a nested object with the original is not a working prototype, it is a second name for the same data.

Shallow or deep

A shallow clone shares nested objects with the template, so a change leaks back. A deep clone owns everything
A shallow clone shares nested objects with the template, so a change leaks back. A deep clone owns everything
Shallow copyDeep copy
Top-level fieldsCopiedCopied
Nested objects and arraysShared by referenceCopied recursively
CostCheapProportional to the size of the data
RiskA change to a nested value shows up in every copySlow for large graphs, trouble with cycles
JavaScript toolsSpread { ...obj }, Object.assign, slice()structuredClone(obj)

Choose deliberately. If nested data is never modified after cloning, sharing it is a fine optimization. If it can change, copy it.

Where it is used in real systems

Use caseWhat is cloned
Duplicate and templates"Duplicate this challenge", a project template, a document from a preset.
Immutable state updatesReact and Redux copy state with spread, then change one field.
Undo and historyA snapshot of the state before each change.
Test fixturesA base object copied and adjusted per test, so tests stay independent.
Game objects and particlesEnemies, bullets and effects cloned from one configured template.
Configuration presetsA base config copied and overridden per environment or tenant.
Caching defaultsA costly default object built once and copied on demand.

Prototypes you already use

JavaScript is built on this idea, which makes it a favorite interview topic.

  • `Object.create(proto)` makes a new object whose prototype is an existing one. Every JavaScript object inherits through a prototype chain.
  • `structuredClone(value)` deep copies plain data, including nested objects, arrays, Map, Set and Date.
  • Spread and `Object.assign` make shallow copies, the everyday form of the pattern in React reducers.

Be careful with JSON.parse(JSON.stringify(value)), the copy trick many people reach for. It quietly drops undefined and functions, turns Date into a string, and throws on cycles. Prefer structuredClone, which keeps those types.

structuredClone has limits too: it cannot copy functions, and class instances come back as plain objects without their methods. That is why classes usually implement their own clone(), as in the example above.

When not to use it

  • Construction is cheap. If new Enemy(...) takes microseconds, cloning buys nothing and adds a method to maintain.
  • Objects hold resources. Open connections, file handles and sockets should not be cloned. Two objects sharing one connection is a bug, and copying a connection is usually impossible.
  • The object graph has cycles or hidden state. Copying it correctly is harder than rebuilding it.
  • Every field must be kept in sync. A hand-written clone() has to be updated each time a field is added. Forget one and clones silently lose data. A test that clones and compares helps.

What interviewers ask

  • "Shallow versus deep copy?" Shallow shares nested references, deep copies them. Show the array example.
  • "How do you deep clone in JavaScript?" structuredClone, and say why not JSON.parse(JSON.stringify(x)).
  • "How does Prototype relate to JavaScript itself?" Objects inherit through prototypes, and Object.create is the pattern built into the language.
  • "When is cloning better than a constructor?" When construction is expensive, or when the state was assembled step by step and you want to reuse it.
  • "What breaks a clone?" Nested references, cycles, and fields added to the class but not to clone().

The mistake to avoid

A Prototype's clone() that copies a reference instead of the value behind it. It is the same shallow-copy bug as the Builder one, one level earlier: whatever a clone shares with its template by reference, every future clone shares too. After writing clone(), mutate a nested field on the copy and check that the original did not change.

Prototype is one naming device among several an object-oriented language gives you for controlling how new gets called. The mechanism above is the same, syntax aside, whether the language is JavaScript, Python or Java.

Now practice it

1 challenge · 2 quizzes

Reading this page is the cheap half. These are the exercises that make you use it.