- Операторы
- Управляющие инструкции
- JS Объекты
- Array
- Boolean
- Date
- Error
- Function
- Global
- JSON
- Math
- Number
- Object
- RegExp
- String
- Symbol
- Итераторы и генераторы
- Map и WeakMap
- Set и WeakSet
- Локализация
- браузер BOM
- HTML DOM
- События
- HTML Объекты
- Промисы, async/await
- Сетевые запросы
- Бинарные данные и файлы
- Разное
Symbol
Символ (Symbol) — примитивный тип, значения которого создаются с помощью вызова функции Symbol
. Каждый созданный символ уникален.
Символы могут использоваться в качестве имён свойств в объектах. Символьные свойства могут быть прочитаны только при прямом обращении и не видны при обычных операциях.
Синтаксис
Symbol([описание])
Параметры
- описание
- Необязательный, строка. Описание символа, которое может быть использовано во время отладки, но не для доступа к самому символу.
Описание, Комментарии
В JavaScript есть два основных типа значений. Первый тип — это примитивы, а второй — объекты (в том числе функции). Примитивы — это простые типы, например числа
(integer
,float
,infiniti
,NaN
), булевские значения, строки,undefined
, иnul
. Примитивные значения являются immutable (т.е. неизменяемыми).Symbol — это примитив, который нельзя создать повторно. В каком-то смысле он подобен объекту, потому что создание нескольких экземпляров приводит к значениям, которые не будут точно равны.
Символы гарантированно уникальны. Даже если мы создадим множество символов с одинаковым описанием, это всё равно будут разные символы. Описание – это просто метка, которая ни на что не влияет.
Например, вот два символа с одинаковым описанием – но они не равны:
let id1 = Symbol("id"); let id2 = Symbol("id"); alert(id1 == id2); // false
Символы не преобразуются автоматически в строки
Если же мы действительно хотим вывести символ с помощью
alert
, то необходимо явно преобразовать его с помощью метода symbol.toString(), вот так:let id = Symbol("id"); alert(id.toString()); // Symbol(id), теперь работает
Или мы можем обратиться к свойству symbol.description, чтобы вывести только описание:
let id = Symbol("Символ id"); alert(id.description);
Если мы хотим использовать символ при литеральном объявлении объекта
{...}
, его необходимо заключить в квадратные скобки:let id = Symbol("id"); let user = { name: "Иннокентий", [id]: 327 // просто "id: 123" не сработает };
Это вызвано тем, что нам нужно использовать значение переменной id в качестве ключа, а не строку «id».
Символы игнорируются циклом
for…in
Свойства, чьи ключи – символы, не перебираются циклом
Например:for..in
.let id = Symbol("id"); let user = { name: "Иннокентий", age: 30, [id]: 327 }; let s=''; for (let key in user) s+=key+'\n'; // name, age (свойства с ключом-символом нет среди перечисленных) // хотя прямой доступ по символу работает s+="Напрямую: " + user[id]; alert(s)
Это – часть общего принципа «сокрытия символьных свойств». Если другая библиотека или скрипт будут работать с нашим объектом, то при переборе они не получат ненароком наше символьное свойство. Object.keys(user) также игнорирует символы.
ВСе символы уникальны, даже если их имена совпадают. Но иногда нужно, чтобы символы с одинаковыми именами были одной сущностью. Например, разные части нашего приложения хотят получить доступ к символу "id", подразумевая именно одно и то же свойство.
Для этого существует глобальный реестр символов. Мы можем создавать в нём символы и обращаться к ним позже, и при каждом обращении нам гарантированно будет возвращаться один и тот же символ. Для создания символов, доступных во всех файлах и в окружении (глобальной области), используются методы Symbol.for() и Symbol.keyFor(), чтобы задать или получить символ из глобального символьного реестра.
Symbol.for()
Метод Symbol.for(key) ищет ранее созданный разделяемый символ по заданному ключу и возвращает его, если он найден. В противном случае создаётся новый разделяемый символ для данного ключа в глобальном реестре символов.
Синтаксис
Symbol.for(key);
Параметры
- key
- Строка, обязательный. Идентификатор символа (также используется в качестве описания символа).
Описание
В противоположность вызову Symbol(), функция Symbol.for() создаёт символ, доступный в глобальном списке реестра символов. Symbol.for() не создаёт новый символ при каждом вызове, вместо этого, метод сначала проверяет, существует ли символ с заданным идентификатором в реестре — и возвращает его, если тот присутствует. Если символ с заданным ключом не найден, Symbol.for() создаст новый глобальный символ.
Глобальный реестр
Глобальный реестр символов — это список со следующей структурой записей и пустой при инициализации:
Поле | Значение |
---|---|
[[key]] | Строка, используемая в качестве идентификатора. |
[[symbol]] | Символ, хранящийся глобально. |
Пример
Symbol.for("foo"); // создаёт новый глобальный символ Symbol.for("foo"); // возвращает символ, созданный прежде // Одинаковый глобальный символ, но не локальный let s = Symbol.for("bar") === Symbol.for("bar"); // true s += '\n' + ( Symbol("bar") === Symbol("bar") ); // false // Идентификатор также используется в качестве описания var sym = Symbol.for("mario"); s += '\n' + ( sym.toString() ); // "Symbol(mario)" alert(s);
Symbol.keyFor()
Метод Symbol.keyFor(sym) получает ключ для заданного символа из глобального реестра символов.
Синтаксис
Symbol.keyFor(sym);
Параметры
- sim
- Символ, обязательный. Символ, ключ которого требуется найти.
Возвращаемое значение
Строка с ключом заданного символа, если он есть в глобальном реестре символов, либо undefined
, если его там нет.
Пример
var globalSym = Symbol.for("foo"); // Создаёт новый глобальный символ let s = Symbol.keyFor(globalSym); // "foo" var localSym = Symbol(); s += '\n' + Symbol.keyFor(localSym); // undefined // Известные символы не является символами, // которые находятся в глобальном реестре символов s += '\n' + Symbol.keyFor(Symbol.iterator) // undefined alert(s);
Системные символы
Существует множество «системных» символов, использующихся внутри самого JavaScript, и мы можем использовать их, чтобы настраивать различные аспекты поведения объектов.
Эти символы перечислены в спецификации в таблице Well-known symbols:
-
Итерационные символы
- Symbol.iterator
Метод, возвращающий итератор по умолчанию для объекта. Используется конструкциейfor...of
.
Символы регулярных выражений
- Symbol.match
Метод для сопоставления объекта со строкой, также используемый для определения возможности объекта выступать в качестве регулярного выражения. Используется функциейString.prototype.match()
. - Symbol.replace
Метод, заменяющий совпавшие подстроки в строке. Используется функциейString.prototype.replace()
. - Symbol.search
Метод, возвращающий индекс вхождения подстроки, соответствующей регулярному выражению. Используется функциейString.prototype.search()
. - Symbol.split
Метод, разбивающий строку на части в местах, соответствующих регулярному выражению. Используется функциейString.prototype.split()
.
Другие символы
- Symbol.hasInstance
Метод, определяющий, распознает ли конструктор некоторый объект как свой экземпляр. Используется операторомinstanceof
. - Symbol.isConcatSpreadable
Булево значение, показывающее, должен ли объект быть сведён к плоскому представлению (англ. flatten) в виде массива его элементов функциейArray.prototype.concat()
. - Symbol.unscopables
Массив строковых имён свойств. Позволяет скрыть свойства от инструкции with (прежде всего для обратной совместимости). - Symbol.species
Метод, определяющий конструктор для порождённых объектов. - Symbol.toPrimitive
Метод, преобразующий объект в примитив (примитивное значение). - Symbol.toStringTag
Строковое значение, используемое в качестве описания объекта по умолчанию. Используется функциейObject.prototype.toString()
Итого
Символ (symbol) – примитивный тип данных, использующийся для создания уникальных идентификаторов.
Символы создаются вызовом функции Symbol()
, в которую можно передать описание (имя) символа.
Даже если символы имеют одно и то же имя, это – разные символы. Если мы хотим, чтобы одноимённые символы были равны, то следует использовать глобальный реестр: вызов Symbol.for(key)
возвращает (или создаёт) глобальный символ с key
в качестве имени. Многократные вызовы команды Symbol.for
с одним и тем же аргументом возвращают один и тот же символ.
Символы имеют два основных варианта использования:
-
«Скрытые» свойства объектов. Если мы хотим добавить свойство в объект, который «принадлежит» другому скрипту или библиотеке, мы можем создать символ и использовать его в качестве ключа. Символьное свойство не появится в
for..in
, так что оно не будет нечаянно обработано вместе с другими. Также оно не будет модифицировано прямым обращением, так как другой скрипт не знает о нашем символе. Таким образом, свойство будет защищено от случайной перезаписи или использования.Так что, используя символьные свойства, мы можем спрятать что-то нужное нам, но что другие видеть не должны.
-
Существует множество системных символов, используемых внутри JavaScript, доступных как
Symbol.*
. Мы можем использовать их, чтобы изменять встроенное поведение ряда объектов.
Технически символы скрыты не на 100%. Существует встроенный метод Object.getOwnPropertySymbols(obj) – с его помощью можно получить все свойства объекта с ключами-символами. Также существует метод Reflect.ownKeys(obj), который возвращает все ключи объекта, включая символьные. Так что они не совсем спрятаны. Но большинство библиотек, встроенных методов и синтаксических конструкций не используют эти методы.