Тип Symbol у JavaScript
Symbol це унікальне та незмінне значення, яке можна використовувати як ключ властивості об'єкта. Кожен Symbol унікальний, навіть якщо в нього такий самий опис, як у іншого символу.
Теорія
TL;DR
Symbolце примітивний тип, поряд зіstring,number,booleanта іншими.- Кожен виклик
Symbol('id')створює нове унікальне значення. - Символ можна використати як ключ властивості:
{ [sym]: value }. - Символьні ключі не видно в
for...in,Object.keys()іJSON.stringify(). - Дістати їх можна через
Object.getOwnPropertySymbols(). Symbol.for()дає глобальний спільний символ,Symbol.keyFor()повертає його ім'я.- Системні символи (
Symbol.iterator,Symbol.toPrimitiveта інші) змінюють поведінку об'єктів.
Швидкий приклад
const id1 = Symbol('id');
const id2 = Symbol('id');
console.log(id1 === id2); // falseПопри однаковий опис 'id', символи не рівні. Кожен Symbol це унікальний ідентифікатор, а опис потрібен лише для зручності налагодження.
Навіщо потрібен Symbol
До появи символів ключами властивостей об'єкта могли бути тільки рядки. Іноді треба додати в об'єкт «приховану» властивість так, щоб вона не заважала решті коду, наприклад стороннім бібліотекам. Саме тут і допомагає Symbol.
const id = Symbol('id');
const user = {
name: 'Alex',
[id]: 123, // символ як ключ
};
console.log(user);
// { name: 'Alex', [Symbol(id)]: 123 }
console.log(user[id]); // 123
console.log(Object.keys(user)); // ['name'], символа не видноСимвольні ключі не беруть участі у for...in та Object.keys() і не конфліктують з іншими властивостями. Це зручно для внутрішніх даних або «приватних» властивостей.
const TOKEN = Symbol('token');
const session = {
user: 'Tim',
[TOKEN]: 'secret-token-123',
};
console.log(session.user); // Tim
console.log(session[TOKEN]); // secret-token-123
console.log(Object.keys(session)); // ['user']Тобто властивість [TOKEN] існує, але вона «невидима» для більшості операцій. Щоб отримати символьні властивості, є окремий метод:
const symbols = Object.getOwnPropertySymbols(session);
console.log(symbols); // [Symbol(token)]
console.log(session[symbols[0]]); // secret-token-123Порівняння Symbol з іншими типами як ключами:
| Тип | Може бути ключем об'єкта | Унікальність | Бере участь у for...in |
|---|---|---|---|
string | Так | Ні | Так |
number | Ні, перетворюється на рядок | Ні | Так, як рядок |
Symbol | Так | Так | Ні |
Symbol не приводиться до рядка неявно
const sym = Symbol('id');
console.log('Мій символ: ' + sym); // TypeErrorЩоб вивести символ як рядок, робіть це явно:
console.log(sym.toString()); // Symbol(id)
console.log(String(sym)); // Symbol(id)
console.log(sym.description); // idГлобальні символи: Symbol.for і Symbol.keyFor
Іноді потрібно, щоб один і той самий Symbol використовувався в різних частинах програми. Для цього є глобальний реєстр символів:
const a = Symbol.for('shared');
const b = Symbol.for('shared');
console.log(a === b); // trueSymbol.for(key)створює новий глобальний символ або повертає вже наявний.Symbol.keyFor(sym)повертає ім'я, під яким символ зареєстровано:
console.log(Symbol.keyFor(a)); // 'shared'Для звичайного Symbol('id'), створеного поза реєстром, Symbol.keyFor() поверне undefined.
Системні символи (well-known symbols)
JavaScript визначає кілька вбудованих символів, які дозволяють перевизначати поведінку об'єктів:
| Символ | Для чого використовується |
|---|---|
Symbol.iterator | Робить об'єкт ітерованим (for...of) |
Symbol.toPrimitive | Керує перетворенням об'єкта на примітив |
Symbol.toStringTag | Визначає ім'я для Object.prototype.toString |
Symbol.hasInstance | Визначає поведінку оператора instanceof |
Symbol.species | Контролює створення похідних класів |
Symbol.asyncIterator | Для асинхронних ітераторів |
Приклад Symbol.iterator:
const numbers = {
data: [1, 2, 3],
[Symbol.iterator]() {
let i = 0;
const arr = this.data;
return {
next() {
return i < arr.length
? { value: arr[i++], done: false }
: { done: true };
}
};
}
};
for (const n of numbers) {
console.log(n); // 1, 2, 3
}Завдяки Symbol.iterator звичайний об'єкт став ітерованим.
Коли застосовувати на практиці
Використовуйте Symbol, коли:
- потрібен унікальний ідентифікатор, який не перетнеться з іншими властивостями;
- треба додати «приватну» властивість в об'єкт або клас;
- треба налаштувати поведінку об'єктів через системні символи (
Symbol.iterator,Symbol.toPrimitiveта інші).
Коротке резюме:
| Що | Опис |
|---|---|
| Тип | Примітивний (symbol) |
| Головна особливість | Унікальність |
| Створення | Symbol('опис') |
| Ключі об'єктів | Можна використовувати як ключ, унікальний і прихований |
| Глобальні символи | Symbol.for() та Symbol.keyFor() |
| Системні символи | Дозволяють перевизначати поведінку JS |
Типові помилки
- Викликати
Symbolчерезnew.new Symbol('id')кидаєTypeError: це примітив, а не конструктор. - Порівнювати символи за описом.
Symbol('id') === Symbol('id')завждиfalse; спільний символ дає лишеSymbol.for('id'). - Неявно склеювати символ з рядком.
'x' + symкидаєTypeError, потрібен явнийString(sym)абоsym.toString(). - Вважати символьні властивості справді приватними. Вони лише приховані від звичайного перебору, але
Object.getOwnPropertySymbols()іReflect.ownKeys()їх покажуть. - Чекати символьні ключі в JSON.
JSON.stringify()їх мовчки викидає, тому дані, потрібні для серіалізації, не варто ховати за символом. - Забувати квадратні дужки.
{ sym: 1 }створить звичайну рядкову властивість'sym', а не символьний ключ; правильно{ [sym]: 1 }.
Коротка відповідь
Для співбесідиКоротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.