Пошук уроків, статей та іншого контенту
Перевірятимете наявність власних властивостей і безпечно працюватимете з назвами, успадкованими з прототипу.
Об’єкт у JavaScript може отримувати властивості двома способами:
власні властивості належать самому об’єкту;
успадковані властивості доступні через його ланцюжок прототипів.
const parent = {
role: "admin"
};
const user = Object.create(parent);
user.name = "Olena";
console.log(user.name); // "Olena" — власна властивість
console.log(user.role); // "admin" — успадкована властивістьПід час звернення user.role JavaScript спочатку шукає role у самому user. Якщо властивості немає, пошук продовжується в прототипі, потім у прототипі прототипу і так далі.
Цей ланцюжок називається ланцюжком прототипів.
inОператор in перевіряє, чи доступна властивість в об’єкті або в одному з його прототипів.
const parent = {
role: "admin"
};
const user = Object.create(parent);
user.name = "Olena";
console.log("name" in user); // true
console.log("role" in user); // true
console.log("email" in user); // falsein повертає true для власних і успадкованих властивостей.
const user = {
name: "Olena"
};
console.log("toString" in user); // truetoString не було явно додано до user, але воно успадковується від Object.prototype.
Оператор in корисний, коли потрібно перевірити, чи властивість доступна взагалі. Проте він не підходить, якщо необхідно перевірити саме власну властивість.
Object.hasOwnСучасний і рекомендований спосіб перевірити власну властивість — Object.hasOwn(object, propertyKey).
const parent = {
role: "admin"
};
const user = Object.create(parent);
user.name = "Olena";
console.log(Object.hasOwn(user, "name")); // true
console.log(Object.hasOwn(user, "role")); // false
console.log(Object.hasOwn(user, "toString")); // falseМетод повертає true, лише якщо властивість безпосередньо записана в об’єкті.
Object.hasOwn працює з назвами властивостей-рядками та символами:
const id = Symbol("id");
const user = {
name: "Olena",
[id]: 42
};
console.log(Object.hasOwn(user, "name")); // true
console.log(Object.hasOwn(user, id)); // truehasOwnPropertyУ багатьох старіших прикладах використовують:
const user = {
name: "Olena"
};
console.log(user.hasOwnProperty("name")); // trueЦей метод зазвичай успадковується від Object.prototype. Але покладатися на нього безпосередньо небезпечно: об’єкт може мати власну властивість із назвою hasOwnProperty.
const data = {
hasOwnProperty: "це вже не метод",
value: 10
};
console.log(data.hasOwnProperty); // "це вже не метод"
// Виклик призведе до помилки:
// data.hasOwnProperty("value");Безпечний варіант для старих середовищ:
console.log(
Object.prototype.hasOwnProperty.call(data, "value")
); // trueЯкщо середовище підтримує Object.hasOwn, краще використовувати саме його.
in і Object.hasOwnРозглянемо практичний приклад:
const defaults = {
theme: "light"
};
const settings = Object.create(defaults);
settings.language = "uk";
console.log("theme" in settings); // true
console.log(Object.hasOwn(settings, "theme")); // false
console.log("language" in settings); // true
console.log(Object.hasOwn(settings, "language")); // trueВибір оператора залежить від завдання:
property in object — властивість доступна в об’єкті або його прототипі;
Object.hasOwn(object, property) — властивість безпосередньо належить об’єкту.
undefined не означає відсутність властивостіНе можна надійно перевіряти наявність властивості лише через порівняння її значення з undefined.
const config = {
timeout: undefined
};
console.log(config.timeout === undefined); // true
console.log(Object.hasOwn(config, "timeout")); // trueВластивість існує, але її значення — undefined.
Тому такі перевірки мають різний зміст:
if (config.timeout === undefined) {
// Значення undefined або властивість відсутня
}
if (!Object.hasOwn(config, "timeout")) {
// Власної властивості timeout немає
}Якщо потрібно розрізнити ці випадки, використовуйте Object.hasOwn.
for...inЦикл for...in перебирає власні та успадковані перелічувані властивості.
const parent = {
role: "admin"
};
const user = Object.create(parent);
user.name = "Olena";
for (const key in user) {
console.log(key);
}
// name
// roleЯкщо потрібно перебрати лише власні властивості, додайте перевірку:
for (const key in user) {
if (Object.hasOwn(user, key)) {
console.log(key);
}
}
// nameObject.keysObject.keys(object) повертає масив лише власних перелічуваних властивостей із ключами-рядками.
const parent = {
role: "admin"
};
const user = Object.create(parent);
user.name = "Olena";
console.log(Object.keys(user)); // ["name"]Цей метод не повертає успадковані властивості та властивості із символічними ключами.
Інші методи для отримання ключів:
Object.keys(object) — власні перелічувані ключі-рядки;
Object.getOwnPropertyNames(object) — усі власні ключі-рядки, зокрема неперелічувані;
Object.getOwnPropertySymbols(object) — власні ключі-символи;
Reflect.ownKeys(object) — усі власні ключі-рядки та символи.
Звичайні об’єкти створюються з прототипом Object.prototype. Через це вони мають доступ до таких властивостей і методів, як:
const value = {};
console.log("toString" in value); // true
console.log("valueOf" in value); // true
console.log("constructor" in value); // true
console.log(Object.hasOwn(value, "toString")); // false
console.log(Object.hasOwn(value, "constructor")); // falseЦе важливо під час роботи з об’єктами як зі словниками. Наприклад, перевірка через in може повідомити, що ключ уже існує, хоча його не додавала ваша програма.
const counters = {};
console.log("constructor" in counters); // true
console.log(Object.hasOwn(counters, "constructor")); // falseДля словників зазвичай потрібна перевірка власних властивостей.
Припустімо, що ключі надходять із зовнішнього джерела:
function getOption(options, key) {
if (Object.hasOwn(options, key)) {
return options[key];
}
return "Значення не знайдено";
}
const options = {
color: "blue"
};
console.log(getOption(options, "color")); // "blue"
console.log(getOption(options, "toString")); // "Значення не знайдено"Така перевірка не дозволяє випадково прийняти успадковану властивість за дані, які справді містяться в об’єкті.
Водночас перевірка власності не замінює валідацію значення:
function getPositiveNumber(data, key) {
if (!Object.hasOwn(data, key)) {
return null;
}
const value = data[key];
if (typeof value !== "number" || value <= 0) {
return null;
}
return value;
}
console.log(
getPositiveNumber({ limit: 10 }, "limit")
); // 10
console.log(
getPositiveNumber({ limit: "10" }, "limit")
); // nullСпочатку перевіряйте наявність ключа, а потім — тип і допустимість його значення.
Для словника можна створити об’єкт без прототипу:
const dictionary = Object.create(null);
dictionary.apple = "яблуко";
dictionary.orange = "апельсин";
console.log(dictionary.apple); // "яблуко"
console.log("toString" in dictionary); // false
console.log(Object.hasOwn(dictionary, "apple")); // trueТакий об’єкт не успадковує властивості від Object.prototype. Це може бути зручно, коли будь-який рядок має бути звичайним ключем словника.
Проте в ньому немає стандартних методів об’єкта:
const dictionary = Object.create(null);
console.log(typeof dictionary.toString); // "undefined"
// dictionary.toString() викликати не можнаДля перебору використовуйте Object.keys, а для перевірки — Object.hasOwn.
Деякі назви мають особливе значення або часто зустрічаються в прототипах:
constructor;
toString;
valueOf;
hasOwnProperty;
__proto__.
Наприклад, __proto__ історично пов’язаний із прототипом об’єкта. Не варто без потреби використовувати його для зміни прототипів або безпосередньої обробки зовнішніх ключів.
Для явної роботи з прототипами використовуйте стандартні API:
const parent = {
role: "admin"
};
const user = Object.create(parent);
console.log(Object.getPrototypeOf(user) === parent); // true
Object.setPrototypeOf(user, null);
console.log(Object.getPrototypeOf(user)); // nullObject.setPrototypeOf може бути корисним у спеціальних випадках, але часту зміну прототипів під час роботи програми краще уникати. Зазвичай прототип задають під час створення об’єкта.
Нижче функція копіює лише дозволені власні властивості з вхідного об’єкта. Успадковані значення ігноруються.
const allowedFields = ["name", "email", "age"];
function pickOwnFields(source, fields) {
const result = {};
for (const field of fields) {
if (Object.hasOwn(source, field)) {
result[field] = source[field];
}
}
return result;
}
const sharedData = {
role: "user"
};
const input = Object.create(sharedData);
input.name = "Olena";
input.email = "olena@example.com";
input.toString = "звичайне текстове значення";
const userData = pickOwnFields(input, allowedFields);
console.log(userData);
// { name: "Olena", email: "olena@example.com" }
console.log(Object.hasOwn(userData, "role")); // false
console.log(Object.hasOwn(userData, "toString")); // falseУ цьому прикладі:
input успадковує role, але ця властивість не копіюється;
копіюються лише ключі з allowedFields;
Object.hasOwn гарантує, що значення записане безпосередньо у input;
невідомі ключі не потрапляють до результату.
Методи, оголошені в класі, зазвичай розміщуються в прототипі, а не в кожному екземплярі.
class User {
constructor(name) {
this.name = name;
}
greet() {
return `Привіт, ${this.name}!`;
}
}
const user = new User("Olena");
console.log(Object.hasOwn(user, "name")); // true
console.log(Object.hasOwn(user, "greet")); // false
console.log("greet" in user); // truename — власна властивість екземпляра, а greet — метод, успадкований через User.prototype.
Це допомагає пояснити, чому Object.keys(user) не показує методи класу:
console.log(Object.keys(user)); // ["name"]in для перевірки данихconst data = {};
if ("toString" in data) {
// Цей блок виконається через Object.prototype
}Якщо потрібен саме ключ із даних, використовуйте:
if (Object.hasOwn(data, "toString")) {
// Власний ключ справді існує
}hasOwnProperty безпосередньоconst data = {
hasOwnProperty: false
};
// data.hasOwnProperty("name") — помилкаБезпечніше:
Object.hasOwn(data, "name");або:
Object.prototype.hasOwnProperty.call(data, "name");const data = {
count: 0,
enabled: false,
value: ""
};
if (data.count) {
// Блок не виконається, хоча count існує
}Наявність властивості та істинність її значення — різні речі:
if (Object.hasOwn(data, "count")) {
console.log("count існує"); // цей код виконається
}Object.keys показує всі доступні властивостіObject.keys не повертає успадковані властивості:
const parent = {
role: "admin"
};
const user = Object.create(parent);
user.name = "Olena";
console.log(Object.keys(user)); // ["name"]
console.log("role" in user); // trueЯкщо потрібні всі властивості з прототипів, їх треба обходити окремо, але в більшості прикладних задач краще явно працювати лише з власними властивостями.
Власна властивість безпосередньо зберігається в об’єкті.
Успадкована властивість доступна через прототип.
in перевіряє власні та успадковані властивості.
Object.hasOwn перевіряє лише власні властивості.
Порівняння з undefined не показує, чи існує властивість.
for...in може перебирати успадковані перелічувані властивості.
Object.keys повертає лише власні перелічувані ключі-рядки.
Для об’єктів-словників корисно використовувати Object.hasOwn або Object.create(null).
Назви на кшталт constructor, toString і hasOwnProperty не слід вважати звичайними без перевірки власності.