Пошук уроків, статей та іншого контенту
Навчитеся створювати об'єкти з указаним прототипом за допомогою Object.create та формувати прототипне наслідування.
Object.createObject.create — це метод JavaScript, який створює новий об’єкт із прототипом, указаним як аргумент.
Прототип — це інший об’єкт, у якому JavaScript може шукати властивості та методи, якщо їх немає безпосередньо в поточному об’єкті.
Синтаксис:
const newObject = Object.create(prototype);Наприклад:
const animal = {
eat() {
console.log("Тварина їсть");
}
};
const dog = Object.create(animal);
dog.eat();Результат:
Тварина їстьУ dog немає власного методу eat, але він успадковує його від об’єкта animal.
Розглянемо простіший приклад:
const userPrototype = {
sayHello() {
console.log(`Привіт, мене звати ${this.name}`);
}
};
const user = Object.create(userPrototype);
user.name = "Олена";
user.sayHello();Результат:
Привіт, мене звати ОленаУ цьому прикладі:
userPrototype містить метод sayHello;
user створений на основі userPrototype;
властивість name додана безпосередньо до user;
метод sayHello знайдений у прототипі;
this усередині методу посилається на об’єкт user.
Коли ми звертаємося до властивості об’єкта, JavaScript діє так:
Перевіряє, чи є властивість безпосередньо в об’єкті.
Якщо властивості немає, шукає її у прототипі.
Якщо у прототипі властивості також немає, продовжує пошук у прототипі прототипу.
Якщо властивість не знайдена, повертає undefined.
const prototypeObject = {
type: "прототип"
};
const object = Object.create(prototypeObject);
object.name = "Об'єкт";
console.log(object.name); // Об'єкт
console.log(object.type); // прототип
console.log(object.color); // undefinedname є власною властивістю object, type успадкована від прототипу, а color не існує ні в об’єкті, ні в його прототипі.
Властивість може бути:
власною — записаною безпосередньо в об’єкті;
успадкованою — отриманою через прототип.
Для перевірки власної властивості зручно використовувати метод Object.hasOwn:
const defaults = {
theme: "light",
language: "uk"
};
const settings = Object.create(defaults);
settings.language = "en";
console.log(settings.theme); // light
console.log(settings.language); // en
console.log(Object.hasOwn(settings, "theme")); // false
console.log(Object.hasOwn(settings, "language")); // trueВластивість theme успадкована від defaults, а language записана безпосередньо в settings.
Якщо в об’єкті створити власну властивість із таким самим ім’ям, як у прототипі, вона приховає успадковану властивість.
const personPrototype = {
role: "користувач"
};
const person = Object.create(personPrototype);
console.log(person.role); // користувач
person.role = "адміністратор";
console.log(person.role); // адміністратор
console.log(personPrototype.role); // користувачВластивість у прототипі не змінюється. В об’єкті person створюється нова власна властивість role.
Цей механізм називається перевизначенням властивості або shadowing.
Створімо прототип для працівників і кілька об’єктів на його основі:
const employeePrototype = {
describe() {
console.log(`${this.name} працює у відділі ${this.department}`);
},
getWorkdayDuration() {
return 8;
}
};
const firstEmployee = Object.create(employeePrototype);
firstEmployee.name = "Андрій";
firstEmployee.department = "Розробка";
const secondEmployee = Object.create(employeePrototype);
secondEmployee.name = "Марія";
secondEmployee.department = "Дизайн";
firstEmployee.describe();
secondEmployee.describe();
console.log(firstEmployee.getWorkdayDuration()); // 8
console.log(secondEmployee.getWorkdayDuration()); // 8Обидва об’єкти використовують ті самі методи з employeePrototype, але мають різні власні дані.
Це і є прототипне наслідування:
спільні методи зберігаються в прототипі;
конкретні значення зберігаються в окремих об’єктах;
об’єкти можуть використовувати властивості та методи прототипу.
Об’єкт може мати прототип, який також має власний прототип. Так утворюється ланцюжок прототипів.
const creature = {
breathe() {
console.log("Дихає");
}
};
const animal = Object.create(creature);
animal.move = function () {
console.log("Рухається");
};
const cat = Object.create(animal);
cat.meow = function () {
console.log("Мяукає");
};
cat.breathe();
cat.move();
cat.meow();Результат:
Дихає
Рухається
МяукаєПошук методів відбувається в такому порядку:
cat;
прототип animal;
прототип creature;
далі — прототип Object.prototype, якщо він є;
якщо властивість не знайдена — undefined.
Якщо передати null, об’єкт буде створено без прототипу:
const dictionary = Object.create(null);
dictionary.apple = "яблуко";
dictionary.book = "книга";
console.log(dictionary.apple); // яблуко
console.log(dictionary.toString); // undefinedЗвичайні об’єкти успадковують методи від Object.prototype, наприклад toString. Об’єкт, створений через Object.create(null), таких успадкованих властивостей не має.
Це може бути корисно для створення простого словника, який містить лише явно додані ключі та значення.
Водночас такий об’єкт потрібно використовувати обережно:
const dictionary = Object.create(null);
dictionary.name = "JavaScript";
console.log(Object.hasOwn(dictionary, "name")); // trueНе слід викликати dictionary.hasOwnProperty(...), оскільки цей метод не буде доступний у об’єкта без прототипу.
Якщо змінити властивість прототипу, зміна буде видима для всіх об’єктів, які її успадковують:
const settingsPrototype = {
language: "uk"
};
const firstSettings = Object.create(settingsPrototype);
const secondSettings = Object.create(settingsPrototype);
console.log(firstSettings.language); // uk
console.log(secondSettings.language); // uk
settingsPrototype.language = "en";
console.log(firstSettings.language); // en
console.log(secondSettings.language); // enОбидва об’єкти читають властивість із прототипу, тому бачать її нове значення.
Якщо додати власну властивість лише одному об’єкту, інший об’єкт не зміниться:
firstSettings.language = "de";
console.log(firstSettings.language); // de
console.log(secondSettings.language); // enДля перевірки того, чи є певний об’єкт прототипом іншого, можна використовувати Object.getPrototypeOf:
const prototypeObject = {
value: 10
};
const object = Object.create(prototypeObject);
console.log(Object.getPrototypeOf(object) === prototypeObject); // trueObject.getPrototypeOf(object) повертає прототип об’єкта.
Також можна перевірити, чи входить об’єкт до ланцюжка прототипів, за допомогою isPrototypeOf:
const parent = {
value: 1
};
const child = Object.create(parent);
console.log(parent.isPrototypeOf(child)); // trueObject.createДругий аргумент Object.create необов’язковий. Він дає змогу одразу створити властивості з певними характеристиками.
const prototypeObject = {
type: "профіль"
};
const profile = Object.create(prototypeObject, {
name: {
value: "Ірина",
writable: true,
enumerable: true,
configurable: true
}
});
console.log(profile.name); // Ірина
console.log(profile.type); // профільОсновні характеристики властивості:
value — значення;
writable — чи можна змінювати значення;
enumerable — чи буде властивість видима під час перебору;
configurable — чи можна видалити або змінити характеристики властивості.
Для початківців зазвичай достатньо створювати властивості звичайним присвоєнням:
const profile = Object.create(prototypeObject);
profile.name = "Ірина";Другий аргумент корисний, коли потрібно точно налаштувати поведінку властивостей.
У наступному прикладі створюється прототип для транспортних засобів і два об’єкти на його основі:
const vehiclePrototype = {
start() {
console.log(`${this.brand} запускається`);
},
describe() {
console.log(
`${this.brand} — ${this.type}, максимальна швидкість: ${this.maxSpeed} км/год`
);
}
};
const car = Object.create(vehiclePrototype);
car.brand = "Toyota";
car.type = "автомобіль";
car.maxSpeed = 180;
const motorcycle = Object.create(vehiclePrototype);
motorcycle.brand = "Honda";
motorcycle.type = "мотоцикл";
motorcycle.maxSpeed = 220;
car.start();
car.describe();
motorcycle.start();
motorcycle.describe();
console.log(Object.hasOwn(car, "start")); // false
console.log(Object.hasOwn(car, "brand")); // true
console.log(Object.getPrototypeOf(car) === vehiclePrototype); // trueМетоди start і describe зберігаються лише в vehiclePrototype, а кожен транспортний засіб має власні значення brand, type і maxSpeed.
Object.create та конструкториObject.create часто використовують для явного створення об’єкта з конкретним прототипом.
const userMethods = {
introduce() {
console.log(`Мене звати ${this.name}`);
}
};
function createUser(name) {
const user = Object.create(userMethods);
user.name = name;
return user;
}
const firstUser = createUser("Олег");
const secondUser = createUser("Наталія");
firstUser.introduce();
secondUser.introduce();Функція createUser створює нові об’єкти, які мають спільний прототип userMethods.
У сучасному JavaScript для подібних задач також часто використовують класи, але Object.create допомагає зрозуміти, як насправді працює прототипне наслідування.
Object.create не копіює властивості прототипу в новий об’єкт.
const prototypeObject = {
value: 10
};
const object = Object.create(prototypeObject);
console.log(Object.hasOwn(object, "value")); // false
console.log(object.value); // 10value доступна через прототип, але не є власною властивістю object.
Якщо властивість не знайдена в дочірньому об’єкті, її зміна може змінити прототип:
const prototypeObject = {
count: 0
};
const firstObject = Object.create(prototypeObject);
const secondObject = Object.create(prototypeObject);
prototypeObject.count = 5;
console.log(firstObject.count); // 5
console.log(secondObject.count); // 5Якщо кожен об’єкт повинен мати власне значення, створіть властивість у самому об’єкті:
firstObject.count = 10;hasOwnProperty без перевіркиУ звичайному об’єкті метод hasOwnProperty зазвичай доступний через Object.prototype, але властивість із таким ім’ям може бути перезаписана:
const object = {
hasOwnProperty: "це вже не метод"
};
console.log(Object.hasOwn(object, "hasOwnProperty")); // trueНадійний сучасний варіант — використовувати Object.hasOwn.
thisМетод, успадкований від прототипу, працює з тим об’єктом, через який його викликали:
const prototypeObject = {
showName() {
console.log(this.name);
}
};
const object = Object.create(prototypeObject);
object.name = "Дмитро";
object.showName(); // ДмитроЗначення this залежить від способу виклику методу, а не лише від місця, де цей метод оголошений.
Створіть прототип productPrototype із методами:
getInfo() — виводить назву та ціну товару;
applyDiscount(percent) — зменшує ціну на вказаний відсоток.
Потім створіть два товари через Object.create:
навушники за ціною 2000;
клавіатуру за ціною 3000.
Викличте методи для обох товарів і перевірте, які властивості є власними, а які успадкованими.
Один із можливих варіантів:
const productPrototype = {
getInfo() {
console.log(`${this.name}: ${this.price} грн`);
},
applyDiscount(percent) {
this.price = this.price - (this.price * percent) / 100;
}
};
const headphones = Object.create(productPrototype);
headphones.name = "Навушники";
headphones.price = 2000;
const keyboard = Object.create(productPrototype);
keyboard.name = "Клавіатура";
keyboard.price = 3000;
headphones.applyDiscount(10);
keyboard.applyDiscount(20);
headphones.getInfo();
keyboard.getInfo();
console.log(Object.hasOwn(headphones, "price")); // true
console.log(Object.hasOwn(headphones, "getInfo")); // falseObject.create(prototype) створює об’єкт із заданим прототипом.
Прототип містить властивості та методи, доступні об’єкту через ланцюжок прототипів.
Власні властивості зберігаються безпосередньо в об’єкті.
Властивість об’єкта може приховати властивість із таким самим ім’ям у прототипі.
Object.hasOwn перевіряє, чи є властивість власною.
Object.getPrototypeOf повертає прототип об’єкта.
Object.create(null) створює об’єкт без прототипу.
Спільні методи доцільно зберігати в прототипі, а індивідуальні дані — у створених об’єктах.