Object.create()
Object.create() - це один із базових методів роботи з прототипами в JavaScript: він створює новий об'єкт, якому ви вручну вказуєте прототип, тобто те, від чого об'єкт успадковує. Другим аргументом можна одразу описати власні властивості дескрипторами.
Теорія
TL;DR
- Синтаксис:
Object.create(proto, [propertiesObject]). protoстає[[Prototype]]нового об'єкта, перевірити можна черезObject.getPrototypeOf().- Другий аргумент задає власні властивості у форматі дескрипторів, як у
Object.defineProperty(). Object.create(null)створює об'єкт зовсім без прототипу, зручний для словників.- Це найпростіший спосіб прототипного наслідування без
classі конструкторів. - Новий об'єкт не копіює властивості прототипу, він лише посилається на нього.
Швидкий приклад
const user = {
sayHi() {
console.log(`Привіт, ${this.name}!`);
}
};
const alice = Object.create(user); // об'єкт, що успадковує від user
alice.name = 'Alice';
alice.sayHi(); // "Привіт, Alice!"Тут user є прототипом ([[Prototype]]) для alice. Власного методу sayHi в alice немає, він успадкований через прототип.
Синтаксис і параметри
Object.create(proto, [propertiesObject])| Параметр | Опис |
|---|---|
proto | Об'єкт, який стане прототипом нового об'єкта (або null) |
propertiesObject (необов'язковий) | Додаткові власні властивості у форматі дескрипторів |
Перевірити результат можна так:
console.log(Object.getPrototypeOf(alice) === user); // trueObject.create() буквально створює новий об'єкт, чий прихований [[Prototype]] дорівнює user.
Об'єкт без прототипу
const data = Object.create(null);
data.a = 1;
console.log(data); // { a: 1 }
console.log(Object.getPrototypeOf(data)); // null
console.log(data.toString); // undefinedТакий об'єкт не успадковує нічого, навіть toString чи hasOwnProperty. Його використовують як чистий словник (map-об'єкт), де ключі приходять ззовні і не повинні конфліктувати з іменами методів Object.prototype. Мінус лише один: перевіряти наявність ключа доведеться через Object.hasOwn(data, key) або 'a' in data.
Другий аргумент: дескриптори властивостей
Властивості можна одразу задати з атрибутами, як у Object.defineProperty():
const person = Object.create({}, {
name: { value: 'Alice', writable: false, enumerable: true },
age: { value: 25, writable: true }
});
console.log(person.name); // "Alice"
person.name = 'Bob'; // не змінить значення
console.log(person.name); // "Alice"Другий параметр приймає ті самі атрибути: value, writable, enumerable, configurable, а також get і set. Пам'ятайте, що пропущені атрибути тут теж дорівнюють false, тому age з прикладу не буде видно в Object.keys().
Прототипне наслідування без класів
Найпряміший спосіб зробити «наслідування» вручну:
const Animal = {
eat() {
console.log('Я їм');
}
};
const Dog = Object.create(Animal);
Dog.bark = function() {
console.log('Гав!');
};
Dog.eat(); // "Я їм", успадковано від Animal
Dog.bark(); // "Гав!", власний методТой самий підхід замінює class, якщо створення екземпляра винести в метод ініціалізації:
const UserProto = {
init(name) {
this.name = name;
return this;
},
greet() {
console.log(`Привіт, ${this.name}`);
}
};
const user = Object.create(UserProto).init('Alice');
user.greet(); // "Привіт, Alice"Такий стиль часто застосовують у функціональному ООП: без класів, але з наслідуванням через прототип.
Підсумок:
| Що робить | Приклад | Результат |
|---|---|---|
| Створює об'єкт із заданим прототипом | Object.create(proto) | Новий об'єкт, у якого [[Prototype]] = proto |
| Створює об'єкт без прототипу | Object.create(null) | «Чистий» об'єкт без toString і hasOwnProperty |
| Задає властивості з атрибутами | Object.create({}, { key: { value: 1 } }) | Аналог defineProperty() |
| Реалізує наслідування | const child = Object.create(parent) | child успадковує властивості parent |
Типові помилки
- Плутати прототип із копією.
Object.create(proto)не копіює властивості: якщо змінитиprotoпізніше, зміни побачить і нащадок. - Шукати успадковані ключі в
Object.keys(). Там лише власні властивості, аfor...inнавпаки обійде і прототипні, тому в циклах потрібенObject.hasOwn(). - Викликати методи
Object.prototypeна об'єкті зObject.create(null).data.hasOwnProperty('a')кинеTypeError; правильноObject.hasOwn(data, 'a')абоObject.prototype.hasOwnProperty.call(data, 'a'). - Забувати про дефолтні
falseу другому аргументі. Властивість, створена черезpropertiesObjectбез явних атрибутів, не змінюється і не перелічується. - Записувати в успадковану властивість і чекати зміни прототипу. Присвоєння створює власну властивість у нащадка і просто затуляє прототипну (shadowing), а не змінює її.
Коротка відповідь
Для співбесідиКоротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.