Skip to main content

Prototype Pattern

Create new objects by copying an existing object (the prototype) rather than creating from scratch.


The Problem​

You have a complex configuration object. You need variations of it, but constructing from scratch is tedious and error-prone.

The naive approach:

const baseConfig = {
server: { host: "localhost", port: 3000, ssl: { enabled: true, cert: "..." } },
database: { host: "localhost", pool: { min: 5, max: 20 } },
logging: { level: "info", format: "json" },
};

// Creating a test config - manual copying is painful
const testConfig = {
server: { ...baseConfig.server, port: 3001 },
database: { ...baseConfig.database, pool: { ...baseConfig.database.pool } },
logging: { ...baseConfig.logging, level: "debug" },
};
// Forgot to deep-copy ssl? Now they share the same object!

Shallow spread doesn't deep-clone. Nested objects are shared. Mutations leak.


The Solution​

Clone the prototype, then modify:

import { deepClone } from "@pithos/core/arkhe";

const baseConfig = {
server: { host: "localhost", port: 3000, ssl: { enabled: true, cert: "..." } },
database: { host: "localhost", pool: { min: 5, max: 20 } },
logging: { level: "info", format: "json" },
};

// Deep clone, then modify safely
const testConfig = deepClone(baseConfig);
testConfig.server.port = 3001;
testConfig.logging.level = "debug";

// baseConfig is untouched - no shared references

True deep copy. Modify freely without affecting the original.


Live Demo​

Clone a config, edit fields in the clone, see the diff. Toggle between "Shallow Copy" and "Deep Clone" β€” shallow copy leaks mutations to the original on nested objects. Deep clone keeps them isolated.

Code
/**
* Prototype pattern: deep clone vs shallow copy.
*
* deepClone from Arkhe creates a true deep copy.
* Spread only copies the top level β€” nested objects are shared references.
*/

import { deepClone } from "@pithos/core/eidos/prototype/prototype";
import type { AppConfig, ConfigPath, CloneMode, RefCheck } from "./types";

export function cloneConfig(config: AppConfig, mode: CloneMode): AppConfig {
if (mode === "deep") return deepClone(config);
return { ...config };
}

export function getField(config: AppConfig, path: ConfigPath): string | number | boolean {
switch (path) {
case "server.port": return config.server.port;
case "server.ssl.enabled": return config.server.ssl.enabled;
case "database.pool.max": return config.database.pool.max;
case "logging.level": return config.logging.level;
}
}

export function setField(config: AppConfig, path: ConfigPath, value: string | number | boolean): void {
switch (path) {
case "server.port": config.server.port = value as number; break;
case "server.ssl.enabled": config.server.ssl.enabled = value as boolean; break;
case "database.pool.max": config.database.pool.max = value as number; break;
case "logging.level": config.logging.level = value as string; break;
}
}

export function checkReferences(original: AppConfig, clone: AppConfig): RefCheck[] {
return [
{ label: "server", shared: original.server === clone.server },
{ label: "server.ssl", shared: original.server.ssl === clone.server.ssl },
{ label: "database", shared: original.database === clone.database },
{ label: "database.pool", shared: original.database.pool === clone.database.pool },
{ label: "logging", shared: original.logging === clone.logging },
];
}
Result

Real-World Analogy​

A document template. You don't write every letter from scratch β€” you copy a template and fill in the specifics. The template is the prototype.


When to Use It​

Whenever you need variations of a complex nested object β€” config overrides, test fixtures, game entity spawning, form presets. If the object has nested references, deepClone guarantees isolation. Shallow spread doesn't.


When NOT to Use It​

If your object is flat (no nesting), the spread operator { ...obj } is simpler and faster. Don't deep-clone when a shallow copy is sufficient.


API​

These functions are from Arkhe and re-exported by Eidos:

  • deepClone β€” Deep clone with common types (objects, arrays, dates, maps, sets)
  • deepCloneFull β€” Deep clone including binary data (TypedArrays, ArrayBuffer, Blob, File)