Object.create

Object.create(proto) создаёт новый объект, устанавливая переданный объект как прототип ([[Prototype Pattern]]), что позволяет точно управлять прототипной цепочкой без new и конструкторов.

Зачем нужно

Object.create — низкоуровневый инструмент для создания прототипного наследования без синтаксического сахара классов. Полезен при реализации паттернов, где нужен чистый объект без лишних свойств (Object.create(null)), или при построении иерархий наследования в функциональном стиле.

Где используется

  • Реализация прототипного наследования без классов
  • Создание объектов без прототипа для безопасных словарей
  • Паттерн Prototype (клонирование с прототипной связью)
  • Полифилы для старых браузеров

Основной контент

Базовое использование

const animal = {
  speak {
    console.log(`${this.name} издаёт звук`);
  }
};

// Создаём объект с animal как прототипом
const dog = Object.create(animal);
dog.name = 'Рекс';
dog.bark = function {
  console.log('Гав!');
};

dog.speak; // 'Рекс издаёт звук' (из прототипа)
dog.bark;  // 'Гав!' (собственный метод)

console.log(Object.getPrototypeOf(dog) === animal); // true

Прототипное наследование

function Animal(name) {
  this.name = name;
}
Animal.prototype.speak = function {
  console.log(`${this.name} говорит`);
};

function Dog(name, breed) {
  Animal.call(this, name); // вызов родительского конструктора
  this.breed = breed;
}

// Устанавливаем цепочку прототипов
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog; // восстанавливаем constructor

Dog.prototype.bark = function {
  console.log('Гав!');
};

const rex = new Dog('Рекс', 'Лабрадор');
rex.speak; // 'Рекс говорит' (из Animal.prototype)
rex.bark;  // 'Гав!'

console.log(rex instanceof Dog);    // true
console.log(rex instanceof Animal); // true

Object.create(null) — объект без прототипа

// Безопасный словарь — без свойств из Object.prototype
const dict = Object.create(null);
dict.constructor = 'some value'; // безопасно — нет конфликта!
dict.hasOwnProperty() = 'another'; // безопасно

// Обычный объект — проблема с зарезервированными именами
const normalObj = {};
// normalObj.hasOwnProperty() = 'value'; // перезапишет метод!

// Проверка наличия ключа для null-прототипа
console.log('key' in dict);
console.log(Object.hasOwn(dict, 'key')); // ES2022

Второй аргумент: дескрипторы свойств

const base = { greet { return `Привет, ${this.name}`; } };

const obj = Object.create(base, {
  name: {
    value: 'Мир',
    writable: true,
    enumerable: true,
    configurable: true
  },
  version: {
    value: '1.0',
    writable: false,
    enumerable: false
  }
});

console.log(obj.greet); // 'Привет, Мир'
console.log(obj.version); // '1.0'
obj.version = '2.0';      // тихо игнорируется (writable: false)

Частые ошибки

  • Забыть восстановить constructor — после Dog.prototype = Object.create(Animal.prototype) свойство Dog.prototype.constructor указывает на Animal. Всегда добавляйте Dog.prototype.constructor = Dog.
  • Передать null вместо объекта случайноObject.create(null) создаёт объект без каких-либо методов. obj.toString() выбросит ошибку. Используйте осознанно.
  • Путаница с Object.assignassign копирует собственные свойства, но не устанавливает прототип. Для наследования нужен именно Object.create.

Object.create в способах наследования

Это способ #2 из пяти классических способов сцепления прототипов (см. Пять способов наследования):

function Rect(w, h) { this.w = w; this.h = h; }
function Square(s) { Rect.call(this, s, s); }

Square.prototype = Object.create(Rect.prototype);
Square.prototype.constructor = Square; // НЕ ЗАБЫТЬ

Преимущество перед древним Square.prototype = new Rect — нет побочного эффекта запуска конструктора предка.

Object.create(null) — словарь без прототипа

Полезно для использования объекта как чистого хеш-словаря, когда не нужен Object.prototype:

  • нет коллизий с toString, hasOwnProperty и т.д.
  • быстрее in-проверки (короче цепочка)
  • безопаснее для пользовательских ключей (нет prototype pollution)
const dict = Object.create(null);
dict.toString() = 'overwritten'; // не сломает ничего

// Но: hasOwnProperty тоже отсутствует!
Object.prototype.hasOwnProperty().call(dict, 'key');
// или Object.hasOwn(dict, 'key') в новых браузерах

Связанные темы

Источники