design·lab

Creational pattern

Prototype

Instead of rebuilding an object from scratch every time, clone an already-configured instance — copy first, tweak second.

✗ The problem

Rebuilding costly, configured objects

Constructing an Enemy means loading a sprite, applying stats, wiring behavior — every single time. Worse: sometimes you only hold an instance at runtime and don't even know its concrete class to call new on.

class Enemy {
  constructor(kind) {
    this.sprite = loadSprite(kind);   // slow I/O
    this.stats  = rollStats(kind);    // heavy calc
    this.buffs  = [];
  }
}
// spawning 50 orcs = 50 full rebuilds, duplicated everywhere
for (let i = 0; i < 50; i++) enemies.push(new Enemy('orc'));
Re-running heavy construction and duplicating setup code at every call site — and if you only have an enemy reference, not the Enemy class, you're stuck.
✓ The pattern

Copy an existing instance via clone()

The object itself knows how to duplicate itself. No class lookup, no re-running setup — just copy the already-built state.

class Enemy {
  constructor(cfg) { this.cfg = cfg; }

  clone() {
    // shallow copy — new object, same nested refs
    return new Enemy({ ...this.cfg });
  }
}
const orcProto = new Enemy({ kind: 'orc', hp: 40 });
const orc2 = orcProto.clone();  // cheap, independent
Prototype instance
fully configured
clone() →
Independent copy
own state
Shallow copy duplicates top-level fields only — nested objects are still shared. Need real independence for nested data? Deep-copy with structuredClone(obj) or a recursive spread.
✓ See it live

Configure a prototype, then clone it

Pick a color + level for the prototype (dashed card), then hit Clone. Each clone is an independent copy — editing the prototype afterwards never touches clones already made.

Color: Level: 1
Prototype
Lv 1
0 clones spawned
✓ Takeaway

Build by copying, not constructing

  • Clone a template instead of rebuilding — great when construction is costly, or when the config is only known at runtime (no class to call new on).
  • Shallow vs deep: spread / Object.assign copy top-level fields only; use structuredClone() or a recursive copy for nested state.
  • Watch out: objects holding references/handles (open sockets, DOM nodes, file handles) can't be blindly cloned — decide per-field what "copy" even means.
🎯 Principle applied: Prototype favors composition/DRY — reuse a configured template instead of duplicating construction — and decouples clients from concrete constructors (Dependency Inversion).