Symbol

Symbol (символ) – це вбудований об'єкт, чий конструктор повертає примітив symbol – котрий також звуть символьним значенням або просто символом, і котрий гарантовано є унікальним. Символи нерідко використовуються для додання до об'єкта унікальних ключів властивостей, котрі не конфліктуватимуть з жодними ключами, які може додати до цього об'єкта якийсь інший код, і котрі приховані від будь-яких механізмів, за допомогою яких інший корд здебільшого буде звертатися до цього об'єкта. Таким чином доступна певного роду слабка інкапсуляція, або ж слабка форма приховування інформації.

Кожний виклик Symbol() гарантовано повертає унікальний символ. Кожний виклик Symbol.for("key") повертає той самий символ для того самого значення "key". Коли викликається Symbol.for("key"), то якщо символ з переданим ключем може бути знайдений у глобальному реєстрі символів — цей символ повертається. Інакше – створюється і додається до глобального реєстру символів за переданим ключем новий символ, після чого – повертається.

Опис

Для створення нового примітивного символу слід написати Symbol() з необов'язковим рядком як його описом:

const sym1 = Symbol();
const sym2 = Symbol("foo");
const sym3 = Symbol("foo");

Код вище створює три нові символи. Зверніть увагу, що Symbol("foo") не зводить рядок "foo" до символу. Він щораз створює новий символ:

Symbol("foo") === Symbol("foo"); // false

Наступний код, з оператором new, викине TypeError:

const sym = new Symbol(); // TypeError

Це не дає розробникам створювати замість нових символьних значень явні об'єкти-обгортки Symbol, але може здивувати, адже загалом створення явних об'єктів-обгорток навколо примітивних типів даних – можливо (наприклад, new Boolean, new String і new Number).

Коли справді треба створити об'єкт-обгортку Symbol, для цього можна використати функцію Object():

const sym = Symbol("foo");
typeof sym; // "symbol"
const symObj = Object(sym);
typeof symObj; // "object"

Через те, що символи – єдиний примітивний тип даних, що має ідентичність за посиланням (тобто не можна створити той самий символ двічі), вони у певному розумінні поводяться як об'єкти. Наприклад, щодо них працює збирач сміття, а тому вони можуть зберігатися в об'єктах WeakMap, WeakSet, WeakRef і FinalizationRegistry.

Спільні символи у глобальному реєстрі символів

Код вище, застосовуючи функцію Symbol(), створює символ, чиє значення залишається унікальним протягом всього часу роботи програми. Для створення символів, доступних у різних файлах і навіть різних царинах (кожна з яких має власну глобальну область видимості), слід застосовувати методи Symbol.for() і Symbol.keyFor() – для задання та отримання символів з глобального реєстру символів.

Зверніть увагу, що "глобальний реєстр символів" є лише уявною концепцією, котра може не відповідати жодній внутрішній структурі даних рушія JavaScript – і навіть коли такий реєстр існує, його вміст недоступний кодові на JavaScript ніяким іншим чином, окрім методів for() і keyFor().

Метод Symbol.for(tokenString) приймає рядковий ключ і повертає символьне значення з реєстру, а Symbol.keyFor(symbolValue) приймає символьне значення і повертає рядковий ключ, котрий цьому значенню відповідає. Ці методи є взаємно оберненими, тож код нижче дає true:

Symbol.keyFor(Symbol.for("tokenString")) === "tokenString"; // true

Через те, що реєстрові символи можуть бути створені будь-де, вони поводяться майже точно так само, як рядки, котрі вони обгортають. Тому такі символи не гарантовано унікальні та не підлягають збиранню сміття. Унаслідок цього реєстрові символи заборонені в об'єктах WeakMap, WeakSet, WeakRef і FinalizationRegistry.

Загальновідомі символи

Усі статичні властивості конструктора Symbol самі є символами, чиї значення – сталі для всіх царин. Вони відомі як загальновідомі символи, і їхнє призначення – служити "протоколами" для певних вбудованих операцій JavaScript, даючи користувачам змогу налаштувати поведінку мови. Наприклад, якщо функція-конструктор має метод з іменем Symbol.hasInstance, то такий метод міститиме логіку для оператора instanceof.

До появи загальновідомих символів JavaScript, для реалізації певних вбудованих операцій, використовував звичайні властивості. Наприклад, функція JSON.stringify намагається викликати на кожному об'єкті метод toJSON(), а функція String викликає на об'єкті методи toString() і valueOf(). Проте, із доданням до мови більшої кількості операцій, виділення кожній з них "магічної властивості" могло зламати зворотну сумісність й ускладнити роботу з логікою мови. Загальновідомі символи дають кастомізаціям змогу бути "невидимими" для звичайного коду, котрий зазвичай зчитує лише рядкові властивості.

На MDN та інших джерелах загальновідомі символьні значення стилізовані за допомогою префікса @@. Наприклад, Symbol.hasInstance записується як @@hasInstance. Це тому, що символи не мають фактичних літеральних форматів, а використання Symbol.hasInstance не відображає можливість використання інших псевдонімів для посилання на той самий символ. Це схоже на різницю між Function.name та "Function".

Загальновідомі символи не мають концепції збирання збирачем сміття, тому що вони входять до фіксованого набору і є унікальними протягом усього життя програми, подібно до вбудованих об'єктів, як то Array.prototype, тож вони також дозволені в об'єктах WeakMap, WeakSet, WeakRef і FinalizationRegistry.

Пошук на об'єктах символьних властивостей

Метод Object.getOwnPropertySymbols() повертає масив символів і дає змогу знайти на переданому об'єкті символьні властивості. Зверніть увагу, що кожний об'єкт ініціалізується без власних символьних властивостей, тож цей об'єкт буде порожнім, якщо не задати на об'єкті якісь символьні властивості.

Конструктор

Symbol()

Створює новий об'єкт Symbol. Це не конструктор в традиційному розумінні, тому що він може бути викликаний лише як функція, але не конструюватися за допомогою new Symbol().

Статичні властивості

Статичними властивостями є загальновідомі символи. У описах цих символів ми використовуємо вирази виду "Symbol.hasInstance – це метод, що визначає…", але майте на увазі, що такі вирази – звертання до семантики методу об'єкта, що містить цей символ як назву свого методу (тому що загальновідомі символи діють як "протоколи"), а не опис значення самого символу.

Symbol.asyncIterator

Метод, що повертає усталений AsyncIterator об'єкта. Використовується циклом for await...of.

Symbol.hasInstance

Метод, що визначає те, чи впізнає об'єкт-конструктор певний об'єкт як свій примірник. Використовується оператором instanceof.

Symbol.isConcatSpreadable

Булеве значення, котре вказує на те, чи сплющується об'єкт до своїх елементів масиву. Використовується Array.prototype.concat().

Symbol.iterator

Метод, що повертає усталений ітератор об'єкта. Використовується циклом for...of.

Symbol.match

Метод, що виконує зіставлення з рядком, а також використовується для з'ясування того, чи може об'єкт вживатися як регулярний вираз. Використовується методом String.prototype.match().

Symbol.matchAll

Метод, котрий повертає ітератор, котрий видає збіги регулярного виразу в рядку. Використовується методом String.prototype.matchAll().

Symbol.replace

Метод, котрий замінює підрядки збігу в рядку. Використовується методом String.prototype.replace().

Symbol.search

Метод, котрий повертає індекс всередині рядка, який дає збіг з регулярним виразом. Використовується методом String.prototype.search().

Symbol.species

Функція-конструктор, котра використовується для створення похідних об'єктів.

Symbol.split

Метод, котрий розбиває рядок за індексами, що дають збіг з регулярним виразом. Використовується методом String.prototype.split().

Symbol.toPrimitive

Метод, котрий перетворює об'єкт на примітивне значення.

Symbol.toStringTag

Рядкове значення, котре використовується як усталений опис об'єкта. Використовується методом Object.prototype.toString().

Symbol.unscopables

Об'єктне значення, чиї власні й успадковані імена властивостей виключаються зі зв'язувань середовища інструкції with для асоційованого об'єкта.

Статичні методи

Symbol.for()

Шукає наявні символи з переданим ключем key і повертає, якщо знайдено. Інакше – в глобальному реєстрі символів створюється новий символ із заданим ключем key.

Symbol.keyFor()

Дістає зі глобального реєстру символів спільний символьний ключ для переданого символу.

Властивості примірника

Ці властивості означені на Symbol.prototype і спільні для всіх примірник Symbol.

Symbol.prototype.constructor

Функція-конструктор, що створила об'єкт-примірник. Для примірників Symbol початковим значенням є конструктор Symbol.

Symbol.prototype.description

Рядок лише для зчитування, що містить опис символу.

Symbol.prototype[@@toStringTag]

Початкове значення властивості @@toStringTag – рядок "Symbol". Ця властивість використовується в методі Object.prototype.toString(). Проте через те, що Symbol має власний метод toString(), то ця властивість не використовується, якщо не викликати Object.prototype.toString.call() з символьним значенням як thisArg.

Методи примірника

Symbol.prototype.toString()

Повертає рядок, що містить опис символу. Заміщає метод Object.prototype.toString().

Symbol.prototype.valueOf()

Повертає той же символ. Заміщає метод Object.prototype.valueOf().

Symbol.prototype[@@toPrimitive]()

Повертає той же символ.

Приклади

Використання з символами оператора typeof

Оператор typeof може допомогти впізнати символи.

typeof Symbol() === "symbol";
typeof Symbol("foo") === "symbol";
typeof Symbol.iterator === "symbol";

Перетворення символьного типу

Кілька речей, котрі слід мати на увазі при перетворенні типу символів.

  • При спробі перетворити символ на число – викидається TypeError (наприклад, +sym або sym | 0).
  • При застосуванні нестрогої рівності, Object(sym) == sym повертає true.
  • Symbol("foo") + "bar" викидає TypeError (не можна перетворювати символ на рядок). Це запобігає, наприклад, тихому створенню з символу нового рядкового імені властивості.
  • "безпечніше" перетворення String(sym) працює як виклик Symbol.prototype.toString() із символами, але зверніть увагу, що new String(sym) викине помилку.

Символи та ітерація for...in

Символи не перелічуються при ітерації for...in. Крім цього, Object.getOwnPropertyNames() не поверне символьних властивостей об'єкта; проте можна застосувати Object.getOwnPropertySymbols() для їх отримання.

const obj = {};

obj[Symbol("a")] = "a";
obj[Symbol.for("b")] = "b";
obj["c"] = "c";
obj.d = "d";

for (const i in obj) {
  console.log(i);
}
// "c" "d"

Символи та JSON.stringify()

Властивості з символьними ключами геть ігноруються при використанні JSON.stringify():

JSON.stringify({ [Symbol("foo")]: "foo" });
// '{}'

Докладніше про це – JSON.stringify().

Об'єкти-обгортки Symbol як ключі властивостей

Коли об'єкт-обгортка Symbol використовується як ключ властивості, то такий об'єкт зводиться до загорнутого в ньому символу:

const sym = Symbol("foo");
const obj = { [sym]: 1 };
obj[sym]; // 1
obj[Object(sym)]; // все одно 1

Специфікації

Сумісність із браузерами

desktop mobile server
Chrome Edge Firefox Internet Explorer Opera Safari WebView Android Chrome Android Firefox for Android Opera Android Safari on iOS Samsung Internet Deno Node.js
Symbol
Chrome Full support 38
Edge Full support 12
footnote
Firefox Full support 36
Internet Explorer No support No
Opera Full support 25
Safari Full support 9
WebView Android Full support 38
Chrome Android Full support 38
Firefox for Android Full support 36
Opera Android Full support 25
Safari on iOS Full support 9
Samsung Internet Full support 3.0
Deno Full support 1.0
Node.js Full support 0.12.0
@@toPrimitive Chrome Full support 47
Edge Full support 15
Firefox Full support 44
Internet Explorer No support No
Opera Full support 34
Safari Full support 10
WebView Android Full support 47
Chrome Android Full support 47
Firefox for Android Full support 44
Opera Android Full support 34
Safari on iOS Full support 10
Samsung Internet Full support 5.0
Deno Full support 1.0
Node.js Full support 6.0.0
Symbol() constructor Chrome Full support 38
Edge Full support 12
Firefox Full support 36
Internet Explorer No support No
Opera Full support 25
Safari Full support 9
WebView Android Full support 38
Chrome Android Full support 38
Firefox for Android Full support 36
Opera Android Full support 25
Safari on iOS Full support 9
Samsung Internet Full support 3.0
Deno Full support 1.0
Node.js Full support 0.12.0
asyncIterator Chrome Full support 63
Edge Full support 79
Firefox Full support 57
Internet Explorer No support No
Opera Full support 50
Safari Full support 11.1
WebView Android Full support 63
Chrome Android Full support 63
Firefox for Android Full support 57
Opera Android Full support 46
Safari on iOS Full support 11.3
Samsung Internet Full support 8.0
Deno Full support 1.0
Node.js Full support 10.0.0
description Chrome Full support 70
Edge Full support 79
Firefox Full support 63
Internet Explorer No support No
Opera Full support 57
Safari Full support 12.1
WebView Android Full support 70
Chrome Android Full support 70
Firefox for Android Full support 63
Opera Android Full support 49
Safari on iOS Full support 12.2
Samsung Internet Full support 10.0
Deno Full support 1.0
Node.js Full support 11.0.0
for Chrome Full support 40
Edge Full support 12
Firefox Full support 36
Internet Explorer No support No
Opera Full support 27
Safari Full support 9
WebView Android Full support 40
Chrome Android Full support 40
Firefox for Android Full support 36
Opera Android Full support 27
Safari on iOS Full support 9
Samsung Internet Full support 4.0
Deno Full support 1.0
Node.js Full support 0.12.0
hasInstance Chrome Full support 50
Edge Full support 15
Firefox Full support 50
Internet Explorer No support No
Opera Full support 37
Safari Full support 10
WebView Android Full support 50
Chrome Android Full support 50
Firefox for Android Full support 50
Opera Android Full support 37
Safari on iOS Full support 10
Samsung Internet Full support 5.0
Deno Full support 1.0
Node.js Full support 6.5.0
isConcatSpreadable Chrome Full support 48
Edge Full support 15
Firefox Full support 48
Internet Explorer No support No
Opera Full support 35
Safari Full support 10
WebView Android Full support 48
Chrome Android Full support 48
Firefox for Android Full support 48
Opera Android Full support 35
Safari on iOS Full support 10
Samsung Internet Full support 5.0
Deno Full support 1.0
Node.js Full support 6.0.0
iterator Chrome Full support 43
Edge Full support 12
Firefox Full support 36
Internet Explorer No support No
Opera Full support 30
Safari Full support 10
WebView Android Full support 43
Chrome Android Full support 43
Firefox for Android Full support 36
Opera Android Full support 30
Safari on iOS Full support 10
Samsung Internet Full support 4.0
Deno Full support 1.0
Node.js Full support 0.12.0
keyFor Chrome Full support 40
Edge Full support 12
Firefox Full support 36
Internet Explorer No support No
Opera Full support 27
Safari Full support 9
WebView Android Full support 40
Chrome Android Full support 40
Firefox for Android Full support 36
Opera Android Full support 27
Safari on iOS Full support 9
Samsung Internet Full support 4.0
Deno Full support 1.0
Node.js Full support 0.12.0
match Chrome Full support 50
Edge Full support 79
Firefox Full support 40
Internet Explorer No support No
Opera Full support 37
Safari Full support 10
WebView Android Full support 50
Chrome Android Full support 50
Firefox for Android Full support 40
Opera Android Full support 37
Safari on iOS Full support 10
Samsung Internet Full support 5.0
Deno Full support 1.0
Node.js Full support 6.0.0
matchAll Chrome Full support 73
Edge Full support 79
Firefox Full support 67
Internet Explorer No support No
Opera Full support 60
Safari Full support 13
WebView Android Full support 73
Chrome Android Full support 73
Firefox for Android Full support 67
Opera Android Full support 52
Safari on iOS Full support 13
Samsung Internet Full support 11.0
Deno Full support 1.0
Node.js Full support 12.0.0
replace Chrome Full support 50
Edge Full support 79
Firefox Full support 49
Internet Explorer No support No
Opera Full support 37
Safari Full support 10
WebView Android Full support 50
Chrome Android Full support 50
Firefox for Android Full support 49
Opera Android Full support 37
Safari on iOS Full support 10
Samsung Internet Full support 5.0
Deno Full support 1.0
Node.js Full support 6.0.0
search Chrome Full support 50
Edge Full support 79
Firefox Full support 49
Internet Explorer No support No
Opera Full support 37
Safari Full support 10
WebView Android Full support 50
Chrome Android Full support 50
Firefox for Android Full support 49
Opera Android Full support 37
Safari on iOS Full support 10
Samsung Internet Full support 5.0
Deno Full support 1.0
Node.js Full support 6.0.0
species Chrome Full support 51
Edge Full support 13
Firefox Full support 41
Internet Explorer No support No
Opera Full support 38
Safari Full support 10
WebView Android Full support 51
Chrome Android Full support 51
Firefox for Android Full support 41
Opera Android Full support 41
Safari on iOS Full support 10
Samsung Internet Full support 5.0
Deno Full support 1.0
Node.js Full support 6.5.0
split Chrome Full support 50
Edge Full support 79
Firefox Full support 49
Internet Explorer No support No
Opera Full support 37
Safari Full support 10
WebView Android Full support 50
Chrome Android Full support 50
Firefox for Android Full support 49
Opera Android Full support 37
Safari on iOS Full support 10
Samsung Internet Full support 5.0
Deno Full support 1.0
Node.js Full support 6.0.0
toPrimitive Chrome Full support 47
Edge Full support 15
Firefox Full support 44
Internet Explorer No support No
Opera Full support 34
Safari Full support 10
WebView Android Full support 47
Chrome Android Full support 47
Firefox for Android Full support 44
Opera Android Full support 34
Safari on iOS Full support 10
Samsung Internet Full support 5.0
Deno Full support 1.0
Node.js Full support 6.0.0
toSource
Non-standard
Chrome No support No
Edge No support No
Firefox No support 36 –  74
footnote
Internet Explorer No support No
Opera No support No
Safari No support No
WebView Android No support No
Chrome Android No support No
Firefox for Android Full support 36
Opera Android No support No
Safari on iOS No support No
Samsung Internet No support No
Deno No support No
Node.js No support No
toString Chrome Full support 38
Edge Full support 12
Firefox Full support 36
Internet Explorer No support No
Opera Full support 25
Safari Full support 9
WebView Android Full support 38
Chrome Android Full support 38
Firefox for Android Full support 36
Opera Android Full support 25
Safari on iOS Full support 9
Samsung Internet Full support 3.0
Deno Full support 1.0
Node.js Full support 0.12.0
toStringTag Chrome Full support 49
Edge Full support 15
Firefox Full support 51
Internet Explorer No support No
Opera Full support 36
Safari Full support 10
WebView Android Full support 49
Chrome Android Full support 49
Firefox for Android Full support 51
Opera Android Full support 36
Safari on iOS Full support 10
Samsung Internet Full support 5.0
Deno Full support 1.0
Node.js Full support 6.0.0
toStringTag available on all DOM prototype objects
Chrome Full support 50
Edge Full support 79
Firefox Full support 78
Internet Explorer No support No
Opera Full support 37
Safari Full support 14
WebView Android Full support 50
Chrome Android Full support 50
Firefox for Android Full support 79
Opera Android Full support 37
Safari on iOS Full support 14
Samsung Internet Full support 5.0
Deno Full support 1.0
Node.js No support No
unscopables Chrome Full support 38
Edge Full support 12
Firefox Full support 48
Internet Explorer No support No
Opera Full support 25
Safari Full support 9
WebView Android Full support 38
Chrome Android Full support 38
Firefox for Android Full support 48
Opera Android Full support 25
Safari on iOS Full support 9
Samsung Internet Full support 3.0
Deno Full support 1.0
Node.js Full support 0.12.0
valueOf Chrome Full support 38
Edge Full support 12
Firefox Full support 36
Internet Explorer No support No
Opera Full support 25
Safari Full support 9
WebView Android Full support 38
Chrome Android Full support 38
Firefox for Android Full support 36
Opera Android Full support 25
Safari on iOS Full support 9
Samsung Internet Full support 3.0
Deno Full support 1.0
Node.js Full support 0.12.0

Дивіться також