Если вы когда-либо искали информацию о типе данных Symbol, то наверняка уже знаете, что Symbol - это уникальный и неизменяемый тип данных, который может быть использован как идентификатор для свойств объектов (MDN). Символы относятся к примитивным типам данным, к которым также относят числа, строки, булевы величины. Однако не стоит забывать, что в JavaScript все является объектом, т.к. и у примитивных типов данных есть свойства и методы.

Зачем нужен тип Symbol?

  1. Избегание коллизии имён - символ уникален даже при совпадающем имени, поэтому переменные типа Symbol не повторяются и не перезаписываются сторонними библиотеками
  2. Символы, как свойства (ключи) объектов - эти свойства недоступны для обычного обхода всех свойств циклом for...in, поэтому могут быть таким образом частично скрыты
  3. Для создания функций-итераторов для неитерируемых изначально объектов.

Объявление переменных типа Symbol

Тип символ объявляется без ключевого слова new. В скобках можно указать какой-либо параметр, но нужно понимать, что особенностью типа данных Symbol является уникальность, поэтому 2 символа с одинаковыми параметрами не будут равны друг другу.

При создании символу можно дать описание (или другими словами имя). Для этого нужно написать Symbol(), указав какую-либо строку в качестве описания этого символа, например let symb2 = Symbol("hello"). Описание – это просто метка, которая ни на что особо не влияет, но помогает идентифицировать символ при отладке в консоли. Однако помните о том, что тип Symbol гарантированно уникален. Даже если вы создадите множество символов с одинаковым описанием, это всё равно будут разные символы.

Создаются новые символы с помощью функции Symbol():

Вы можете получить описание символа с помощью свойства Symbol.description:

Если вы попытаетесь объявить символ с помощью ключевого слова new, то увидите ошибку в консоли браузера:

Обратите внимание, что ошибка появляется в этом случае только при попытке создать именно тип Symbol с помощью ключевого слова new. Для других примитивных типов создание явных объектов-обёрток вполне работоспособно, например: new Boolean(true)new String("My String")new Number(456).

Иными словами —  символа нужен именно в качестве уникального идентификатора, а new возвращает объект; объект же здесь не нужен.

Если вам по какой-то причине необходимо, чтобы символ был объектом, то придется обернуть символ в объект, используя функцию Object():

Глобальные символы

Для создания символов, доступных во всех файлах и в глобальной области, вы можете использовать методы Symbol.for() и Symbol.keyFor(), чтобы задать или получить символ из глобального символьного реестра. То есть такие  глобальные символы доступны во всех частях вашей программы.

Даже, если вы создадите 2 символа с одинаковым ключом, в реальности обе переменные будут вести к одному и тому же символу, т.е. это будет уникальная, но одна и та же переменная. Поэтому переменные будут равны, в отличие от тех, которые создавались с одинаковым описанием. Для этого нам понадобится метод Symbol.for():

Если вам нужно получить значение ключа для символа, используйте метод Symbol.keyFor():

В результате использования метода Symbol.keyFor() вы получите строку с ключом указанного символа, если он есть в глобальном реестре символов, либо undefined, если он там отсутствует.

Метод toString()

Символы - это особый тип данных, и они автоматически не преобразуются в строку. Однако, если нужно получить строковое представление символа, то нужно использовать метод toString():

Доступ к символам, как ключам объекта

Поскольку символы - это уникальные идентификаторы, которые не изменяются извне при совпадении имени переменной, в качестве значения которой они выступают, их можно использовать в ситуации, когда возможна перезапись одной переменной другой. Если другая библиотека или внешний скрипт будут работать с вашим объектом, то при переборе свойств этого объекта, доступ к символьному свойству будет закрыт. Метод Object.keys(some_object) также игнорирует символы.

Мы можем указать символ в качестве ключа (поля, или свойства) объекта:

Обратите внимание, что вы не можете использовать оператор точки для доступа или создания ключей-символов, потому что он работает только с ключами-строками. В данном случае для создания/доступа к свойству нужно использовать квадратные скобки.

При выводе в консоль мы увидим свойство id и info. То же самое, но уже без свойства id и без свойства info мы увидим, если попробуем в ту же консоль вывести каждое свойство с помощью цикла for...in:

То есть то свойство, которое имеет ключ  в виде символа, не выводится в цикле. Если вам нужно получить только те свойства, которые имеют тип Symbol, вы можете использовать метод объекта Object, который для этого предназначен -  Object.getOwnPropertySymbols(objectName):

Метод Object.getOwnPropertySymbols()возвращает массив символов и позволяет найти свойства символов для данного объекта.

Метод Symbol.iterator()

Symbol.iterator()  - это, пожалуй, самый используемый метод из тех, которые есть у символов. Он возвращает итератор по умолчанию для объекта. Symbol.iterator()  позволяет перебирать элементы в массивах и массивоподобных объектах. Они еще называются итерируемыми. Это такие объекты, как Array, String, MapSet, объект аргументов функции arguments, коллекции элементов на странице и т.д. Итерируемым объектом является любой объект, который реализует интерфейс Iterable, то есть такой, у которого есть метод Symbol.iterator, возвращающий объект с методом next().

Сложно? Давайте разберемся с этим методом в несколько этапов.

Для того чтобы понять, что такое итератор, напишем функцию, которая будет проходить по массиву и возвращать следующее значение массива до тех пор, пока это будет возможно, т.е. до последнего элемента включительно. В случае, если значение можно получить, внутренняя функция next() возвращает значение элемента массива и метку done со значением false, а если значение недоступно (превышен номер индекса), то значение будет равно undefined, а метка done будет true.

В коде мы передаем в функцию массив и проходим по нему с помощью вложенной функции next():

Однако дело в том, что такие сложности с массивами излишни. Они из коробки (т.е. из ядра JavaScript) имеют встроенный итератор, который позволяет перебирать их значения в цикле for...of .  Он появился в JavaScript как раз для обхода любых итерируемых объектов, и имеет возможность прерывания его выполнения оператором break.

Допустим, нам нужно сделать итерируемым некий объект, в котором есть свойства в виде чисел, т.е. некий диапазон, по которому мы должны пройтись с неким шагом. В качестве функции, осуществляющей обход, будем использовать Symbol.iterator:

Генераторы. Как использовать для итерируемых объектов?

Генератор - это особый вид функций, который может приостанавливать своё выполнение, возвращать промежуточный результат, а также возобновлять своё выполнение в произвольный момент времени. То есть это замена той функции, которая у нас генерировала  next(), но с особым синтаксисом.

Объявление функции-генератора:

Как работает генератор?

  1. someGenerator() при вызове функции next() вернёт объект-генератор с данными в виде {value: 'JavaScript', done: false}
  2. Генераторы используют специальный оператор yield для возврата данных.
  3. Оператор yield отслеживает предыдущие вызовы и просто продолжает работу функции с последнего места прерывания.
  4. Если мы используем yield внутри цикла, то он будет выполняться только один раз, когда будет вызываться метод next() .

Например, мы можем объявить простой генератор в качестве итератора для пустого объекта:

Несколько иначе будет выглядеть итерация объекта:

В коде выше функция-генератор предназначена для обхода объекта циклом for...of.

Кроме цикла for...of, JavaScript использует Symbol.iterator в следующих конструкциях: spread-оператор, yielddestructuring assignment - деструктивное присваивание.

Больше о генераторах вы можете узнать в статье "Использование генераторов в JavaScript на примерах"

Автор: Админ

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *