Пошук уроків, статей та іншого контенту
Розберете конструкторні функції, оператор new і автоматичне налаштування прототипу створеного об'єкта.
Конструкторна функція — це звичайна функція, яку використовують як шаблон для створення однотипних об’єктів.
За домовленістю назву конструкторної функції пишуть з великої літери:
function User(name, age) {
this.name = name;
this.age = age;
}Тут User описує, які властивості матимуть об’єкти користувачів. Ключове слово this посилається на конкретний об’єкт, який буде створено пізніше.
newДля створення об’єкта за допомогою конструкторної функції використовують оператор new:
function User(name, age) {
this.name = name;
this.age = age;
}
const firstUser = new User("Олена", 25);
const secondUser = new User("Андрій", 31);
console.log(firstUser.name); // Олена
console.log(secondUser.age); // 31Кожен виклик new User(...) створює окремий об’єкт:
console.log(firstUser === secondUser); // falseЗміна властивостей одного об’єкта не впливає на інший:
firstUser.age = 26;
console.log(firstUser.age); // 26
console.log(secondUser.age); // 31newВиклик:
const user = new User("Олена", 25);можна уявляти як послідовність кількох автоматичних дій:
Створюється новий порожній об’єкт.
Прототипом цього об’єкта стає User.prototype.
Усередині функції User значення this посилається на новий об’єкт.
Виконується код функції-конструктора.
Новий об’єкт повертається як результат виразу.
Тому цей код:
function User(name, age) {
this.name = name;
this.age = age;
}
const user = new User("Олена", 25);приблизно відповідає такій логіці:
const user = Object.create(User.prototype);
User.call(user, "Олена", 25);У реальному JavaScript оператор new також має правила обробки значення, яке явно повертає конструктор. На початковому рівні важливо запам’ятати: зазвичай конструктор налаштовує this, а new повертає створений об’єкт.
prototypeКожна звичайна функція має властивість prototype. Це об’єкт, який використовується як прототип для об’єктів, створених через new.
function User(name) {
this.name = name;
}
const user = new User("Олена");
console.log(Object.getPrototypeOf(user) === User.prototype); // trueЗавдяки цьому всі створені користувачі можуть використовувати спільні методи з User.prototype.
Методи краще додавати до прототипу, а не створювати всередині конструктора:
function User(name, age) {
this.name = name;
this.age = age;
}
User.prototype.sayHello = function () {
return `Привіт! Мене звати ${this.name}.`;
};
User.prototype.isAdult = function () {
return this.age >= 18;
};
const firstUser = new User("Олена", 25);
const secondUser = new User("Андрій", 16);
console.log(firstUser.sayHello()); // Привіт! Мене звати Олена.
console.log(firstUser.isAdult()); // true
console.log(secondUser.isAdult()); // falseМетоди знаходяться в одному місці — у User.prototype. Створені об’єкти знаходять їх через ланцюжок прототипів.
Можна написати так:
function User(name) {
this.name = name;
this.sayHello = function () {
return `Привіт! Мене звати ${this.name}.`;
};
}Такий код працює, але для кожного об’єкта створюється нова функція sayHello.
Якщо метод додати до прототипу:
function User(name) {
this.name = name;
}
User.prototype.sayHello = function () {
return `Привіт! Мене звати ${this.name}.`;
};усі об’єкти використовуватимуть одну спільну функцію:
const firstUser = new User("Олена");
const secondUser = new User("Андрій");
console.log(firstUser.sayHello === secondUser.sayHello); // trueВластивості, створені через this, належать конкретному об’єкту:
function User(name) {
this.name = name;
}
const firstUser = new User("Олена");
const secondUser = new User("Андрій");
console.log(firstUser.hasOwnProperty("name")); // true
console.log(secondUser.hasOwnProperty("name")); // trueМетод, доданий до prototype, не є власною властивістю кожного об’єкта:
function User(name) {
this.name = name;
}
User.prototype.sayHello = function () {
return `Привіт, ${this.name}!`;
};
const user = new User("Олена");
console.log(user.hasOwnProperty("sayHello")); // false
console.log("sayHello" in user); // trueОператор in перевіряє весь ланцюжок прототипів, тому знаходить метод.
instanceofОператор instanceof перевіряє, чи належить об’єкт до екземплярів певного конструктора:
function User(name) {
this.name = name;
}
const user = new User("Олена");
console.log(user instanceof User); // true
console.log(user instanceof Object); // trueuser instanceof User повертає true, тому що прототип User.prototype є частиною прототипного ланцюжка об’єкта user.
Конструктор може не мати параметрів:
function Counter() {
this.value = 0;
}
Counter.prototype.increment = function () {
this.value += 1;
};
const counter = new Counter();
counter.increment();
counter.increment();
console.log(counter.value); // 2Зазвичай конструктор нічого не повертає явно:
function User(name) {
this.name = name;
}У такому випадку new повертає створений об’єкт.
Якщо конструктор явно повертає інший об’єкт, саме цей об’єкт буде результатом:
function User(name) {
this.name = name;
return {
name: "Інше ім’я"
};
}
const user = new User("Олена");
console.log(user.name); // Інше ім’яЯкщо конструктор повертає примітивне значення, наприклад рядок або число, воно ігнорується:
function User(name) {
this.name = name;
return 42;
}
const user = new User("Олена");
console.log(user.name); // ОленаЯвне повернення об’єкта в конструкторі використовують рідко. Зазвичай конструктор просто налаштовує this.
newКонструкторну функцію потрібно викликати з new:
function User(name) {
this.name = name;
}
const user = new User("Олена");Якщо викликати її без new, результат може бути несподіваним:
const user = User("Олена");У строгому режимі this усередині функції буде undefined, і спроба записати this.name спричинить помилку.
Для захисту можна перевіряти new.target:
function User(name) {
if (!new.target) {
throw new Error("Функцію User потрібно викликати з оператором new");
}
this.name = name;
}
const user = new User("Олена");
console.log(user.name); // Оленаnew.target містить значення конструктора, якщо функцію викликали через new. Без new це значення дорівнює undefined.
Стрілкові функції не можна використовувати з оператором new:
const User = (name) => {
this.name = name;
};
// const user = new User("Олена"); // TypeErrorПричина в тому, що стрілкові функції не мають власного this і не є конструкторами.
Для конструкторної функції використовуйте звичайне оголошення функції:
function User(name) {
this.name = name;
}
const user = new User("Олена");function Product(name, price) {
this.name = name;
this.price = price;
}
Product.prototype.getInfo = function () {
return `${this.name}: ${this.price} грн`;
};
Product.prototype.applyDiscount = function (percent) {
this.price -= this.price * percent / 100;
};
const laptop = new Product("Ноутбук", 30000);
const phone = new Product("Смартфон", 18000);
laptop.applyDiscount(10);
console.log(laptop.getInfo()); // Ноутбук: 27000 грн
console.log(phone.getInfo()); // Смартфон: 18000 грн
console.log(laptop instanceof Product); // trueУ цьому прикладі:
Product — конструкторна функція;
name і price — власні властивості кожного об’єкта;
getInfo і applyDiscount — спільні методи прототипу;
new Product(...) створює нові об’єкти;
instanceof перевіряє належність об’єкта до конструктора Product.
newfunction User(name) {
this.name = name;
}
// Неправильно:
const user = User("Олена");
// Правильно:
const user = new User("Олена");Без new функція не працює як конструктор.
Технічно це не помилка JavaScript, але така назва порушує загальну домовленість:
function user(name) {
this.name = name;
}Краще:
function User(name) {
this.name = name;
}Велика літера підказує іншим розробникам, що функцію потрібно викликати через new.
thisМетод прототипу використовує this для доступу до властивостей конкретного об’єкта:
function User(name) {
this.name = name;
}
User.prototype.sayHello = function () {
return `Привіт, ${this.name}!`;
};
const user = new User("Олена");
console.log(user.sayHello()); // Привіт, Олена!Якщо передати метод окремо, він може втратити потрібний контекст:
const sayHello = user.sayHello;
// sayHello(); // this не посилається на userНа цьому етапі викликайте метод через сам об’єкт:
user.sayHello();prototypeНеобережне присвоєння нового об’єкта до prototype може змінити властивість constructor:
function User(name) {
this.name = name;
}
User.prototype = {
sayHello: function () {
return `Привіт, ${this.name}!`;
}
};Для простих прикладів зручніше додавати методи окремими властивостями:
User.prototype.sayHello = function () {
return `Привіт, ${this.name}!`;
};Конструкторна функція — це шаблон для створення однотипних об’єктів.
Конструкторні функції зазвичай називають з великої літери.
Оператор new створює об’єкт і передає його в конструктор як this.
Прототип створеного об’єкта автоматично стає рівним Constructor.prototype.
Спільні методи доцільно додавати до prototype.
instanceof перевіряє зв’язок об’єкта з конструктором.
Стрілкові функції не можна використовувати з оператором new.
Конструкторну функцію потрібно викликати через new.