Prototype Pattern (GoF) — Прототип
Клонирование готового экземпляра вместо инициализации нового. В GoF — один из самых базовых паттернов, в JS — встроенное через
structuredClone.
Проблема
Создание объекта дорогое: тяжёлая инициализация, обращение к БД, сложные вычисления. Если нужно много похожих объектов — выгоднее склонировать готовый.
Где используется
- Клонирование конфигурационных объектов
- Создание объектов на основе шаблона по умолчанию
- Игровые объекты (NPC, предметы с похожими параметрами, тысячи врагов)
- Иммутабельные обновления в Redux
HTMLElement.cloneNode(true)— клон DOM-узла
Решение
Делается эталонный объект. Все остальные создаются через клонирование, потом точечно модифицируются.
В JS есть несколько способов:
structuredClone(obj)— нативный глубокий клон (рекомендуемый, Node.js 17+)JSON.parse(JSON.stringify(obj))— старый трюк (не работает для функций, дат)Object.create(proto)— наследование через прототип- Собственная функция
deepClone
Реализации
structuredClone (рекомендуемый)
const prototype = {
type: 'enemy',
hp: 100,
damage: 10,
attack(target) { target.hp -= this.damage; },
};
// тысячи врагов: каждый — клон прототипа
const enemies = Array.from({ length: 1000 }, () => structuredClone(prototype));
enemies[0].hp = 50;
Object.create
const vehiclePrototype = {
init(make, model) { this.make = make; this.model = model; return this; },
getInfo { return `${this.make} ${this.model}`; },
clone { return Object.create(this); }
};
const car = Object.create(vehiclePrototype).init('Toyota', 'Camry');
const clone = car.clone();
clone.make = 'Honda';
console.log(clone.getInfo); // 'Honda Camry'
console.log(car.getInfo); // 'Toyota Camry'
Через метод clone в классе
class Shape {
constructor(color, size) {
this.color = color;
this.size = size;
this.metadata = { created: new Date };
}
clone {
const c = Object.assign(Object.create(Object.getPrototypeOf(this)), this);
c.metadata = { ...this.metadata }; // глубокий клон вложенного
return c;
}
draw { return `${this.color} ${this.size}`; }
}
class Circle extends Shape {
constructor(color, size, radius) { super(color, size); this.radius = radius; }
clone {
const c = super.clone();
c.radius = this.radius;
return c;
}
}
const original = new Circle('red', 'large', 50);
const cloned = original.clone();
cloned instanceof Circle; // true
Реестр прототипов
class PrototypeRegistry {
constructor { this._registry = {}; }
register(name, proto) { this._registry[name] = proto; }
create(name, overrides = {}) {
const p = this._registry[name];
if (!p) throw new Error(`'${name}' not registered`);
return { ...p, ...overrides };
}
}
const registry = new PrototypeRegistry();
registry.register('button', { type: 'button', disabled: false, className: 'btn' });
const primary = registry.create('button', { className: 'btn btn-primary' });
Где используется в JS-экосистеме
- Геймдев: множество однотипных объектов (враги, частицы)
- Redux: иммутабельные обновления
{ ...state, ...changes } - React: deep clone state перед мутацией
- HTMLElement.cloneNode(true) — клон DOM-узла
Подводные камни
JSON.parse(JSON.stringify(x))теряет: функции,Date,Map,Set,undefined, циклы.structuredCloneдоступен в Node.js 17+ и современных браузерах — корректнее JSON.- Глубокий клон дорогой: для иммутабельных обновлений лучше shallow copy + новый объект.
- Поверхностное клонирование:
Object.assignи spread{...obj}копируют только первый уровень. - Потеря цепочки прототипов:
JSON.parse(JSON.stringify(obj))создаёт plain object без прототипа. - Не путать с прототипным наследованием JS — это разные вещи (хотя связь есть).
- GoF Prototype vs JS prototype chain: GoF Prototype — паттерн клонирования;
[[Prototype Pattern]]в JS — механизм наследования.
Главные тезисы автора
- «Люди говорят, JS = прототипное программирование, паттерн не нужен. На самом деле нет.»
JSON.parse(JSON.stringify)— ближе к Prototype, чем к прототипной цепочке.- Prototype экономит память и вычисления — переиспользует структуру.
- Прототипные цепочки JS ближе к Flyweight, а не к Prototype.
🎓 Источники
- 🎓 GoF Patterns Обзор всех паттернов · 2025-04-29
- Prototype — клонирование вместо реинициализации
- JSON.parse/stringify, structuredClone, deep copy
- Разница между Prototype и Flyweight в JS
- refactoring.guru — Prototype
- MDN — Object.create
См. также
- Flyweight Pattern — общее состояние через прототипы
- Factory Pattern
- Object.create()
- Property Descriptors
- _MOC GoF