Symbol javascript для чего нужен
JavaScript Symbols. Новый тип примитивов
Sep 11, 2019 · 7 min read
Введение
Например, в языке C и некоторых других есть понятие pass-by-reference и pass-by-value (передача аргументов в функцию по ссылке или по значению). В JavaScript тоже есть такое понятие в каком-то смысле, хотя всё зависит от типа передаваемых данных. Если вы передадите значение в функцию, то оно не изменится в вызываемом местоположении, даже если вы переназначите это значение. В случае с «непримитивным» значением, оно изменится и там, откуда был вызов.
Примитивные значения (за исключением мистического NaN ) всегда будут точно равны другим примитивам с эквивалентным значением. Пример:
Тем не менее создание эквивалентных непримитивных значений не приведёт к точному равенству значений. Это показывает следующий пример:
Объекты — это фундаментальная единица в JavaScript. Они используются везде. Объекты часто используются в качестве коллекций пар ключ/значение. Но это накладывает определённые ограничения. До появления символов, ключи объектов могли быть только строками. Если попытаться использовать «нестроковое» значение в качестве ключа для объекта, оно будет приведено к строке. Можете увидеть это на примере:
Примечание: это не совсем по теме, но структура данных Map была создана отчасти, чтобы позволить хранить пары ключ/значение в случае, когда ключ не является строкой.
Что такое Symbol?
Теперь, когда мы вспомнили, что такое примитивное значение, давайте поговорим о символах. Symbol — это примитив, который нельзя создать повторно. В каком-то смысле он подобен объекту, потому что создание нескольких экземпляров приводит к значениям, которые не будут точно равны. Но всё же, символ — это примитив, потому что он не может быть изменён. Пример использования:
У символов есть опциональный первый аргумент, который можно указать как строку. Это значение используют при отладке кода. В остальном оно не влияет на сам символ.
Symbol как свойство объекта
Есть и другое важное назначение символов. Их можно использовать в качестве ключей в объектах. Вот пример такого использования:
На первый взгляд кажется, что символы можно использовать для создания приватных свойств объекта. В других языках для этого есть скрытые свойства в классах. Отсутствие такой возможности всегда считалось недостатком JavaScript.
Примечание: в настоящее время разработчики решают проблему, связанную с добавлением приватных свойств в классы JavaScript. Эта фича называется Private Fields, и, хотя она не решит всех проблем, зато поможет работать с объектами, которые являются экземплярами класса. Private Fields поддерживаются в Chrome 74.
Предотвращение конфликтов в именах свойств
Символы не предоставляют полной приватности свойств, но они полезны в ситуациях, когда разным библиотекам необходимо добавить свойства к объектам без риска возникновения конфликтов имён.
Рассмотрим ситуацию, когда метаданные из двух разных библиотек нужно присоединить к объекту. Допустим, требуется установить идентификатор для объекта. Если просто задать ключ из двух букв строчного типа ( id ), существует огромный риск того, что несколько библиотек будут использовать один и тот же ключ.
Благодаря символам каждая библиотека может генерировать необходимые ей символы при создании экземпляра. Далее эти символы можно использовать для назначения свойств объектам.
Для подобных целей символы оказываются действительно полезными в JavaScript.
Но почему каждая библиотека не может просто генерировать случайную строку или использовать строку из специального пространства имён?
Звучит разумно. Такой подход очень похож на то, что происходит при использовании символов. Пока две библиотеки используют разные имена свойств, проблем не возникнет.
Здесь внимательный читатель заметил, что эти два подхода не были полностью эквивалентны. В именах свойств, где эти имена уникальны, всё ещё есть недостаток: их ключи очень легко обнаружить, особенно если код выполняется для итерации ключей, либо для сериализации объектов. Рассмотрим следующий пример:
Если бы в этой ситуации для имени ключа использовался символ, то JSON не содержал бы его значение на выходе. Почему? Потому что спецификация JSON не изменилась. JSON в качестве ключей допускает только строки, и JavaScript не представит свойства символов в конечной полезной нагрузке JSON.
Исправить проблему попадания имён свойств в вывод JSON можно с помощью Object.defineProperty() :
Но, есть ещё одна маленькая разница. Поскольку строки неизменяемы, а символы гарантированно уникальны, всегда есть возможность того, что кто-то, перебрав все возможные сочетания символов в строке, вызовет конфликт имён. Математически это показывает, что символы дают преимущество перед строками.
Имитация приватных свойств
Есть интересный подход для имитации приватных свойств объекта. Этот подход использует другую JavaScript функцию, доступную уже сегодня: прокси. Прокси, по сути, обёртывает объект и позволяет взаимодействовать с ним через себя.
У прокси есть множество способов перехватывать действия, выполняемые над объектом. Тот, который нас интересует, способен перехватывать попытки чтения ключей объекта.
Прокси можно использовать для управления тем, какие свойства объекта видны. Далее мы создадим прокси, который будет скрывать два известных свойства, одно из которых является строкой _favColor, а другой символом, назначенным favBook:
Осваиваем JavaScript ES6 Symbol
Кредит под залог недвижимости в Краснодаре
Компания АСВ Финанс https://asv-finance.ru/
предлагает практически всех виды займов: под залог земельных участков, домов, квартир, автомобилей.
Так же можем осуществить проверку чистоты интересующего вас объекта недвижимости, либо автомобиля.
JavaScript — один из основных стандартов JavaScript, также известный как ECMAScript, был введен в 1997 году. С тех пор в языке присутствовали только следующие базовые типы.
Null помечен как одно из примитивных значений в JavaScript, поскольку его действие очевидно. Но в некоторых ситуациях null не так «примитивен», как кажется на первый взгляд! Каждый объект является производным от null, и поэтому оператор typeof возвращает для него тип Объект.
Но с выпуском ES6 в 2015 году был добавлен еще один новый базовый тип — Symbol. Он сильно отличается от предыдущих типов. Этот тип представляет собой просто значение, а не строку, число или даже объект.
Что это за новый тип данных?
Тип данных Symbol — это прежде всего уникальное значение. Его значение — уникальный идентификатор. Вы можете просто вызвать Symbol() и получить уникальный идентификатор. При желании вы также можете передать описание.
Одна из ключевых вещей, которую вы должны помнить, — это то, что Symbol всегда уникальны. Даже если вы передадите одно и то же описание двум Symbol, они все равно будут разными.
Многие люди думают о Symbol как о способе получения уникального значения. Но это только отчасти правда. Хотя Symbol уникальны, вы никогда не получите уникальное значение, отобразив его в консоль. Вы можете только назначить его переменной и использовать эту переменную как уникальный идентификатор.
Другими словами, Symbol не дает уникального значения, такого как идентификатор, который может выглядеть как то так 285af1ae40223348538204f8c3a58f34. Но, скорее, когда вы наберете в консоли Symbol, вы получите Symbol() или Symbol(описание). Помните, что это будет не строка, а простой Symbol.
Вы можете получить строку, вызвав метод toString() для объекта Symbol. Но это тоже даст вам только строковое представление ранее полученного значения.
Что нужно иметь в виду
Нет автоматического преобразования в строку
window.alert() получает параметр строкового типа. Но даже если вы передадите число или даже ноль, вы не получите ошибку. Скорее JavaScript неявно преобразует тип данных в строку и отобразит ее. Но в случае с Symbol JavaScript не преобразует его неявно в строку. Эти два элемента должны быть разделены, поскольку они принципиально разные и не должны случайно превращаться друг в друга.
Использование символов в качестве ключей в литерале объекта
Взгляните на приведенный ниже код.
В приведенном выше примере, хотя мы назначили свойство id нашему объекту obj, это не та переменная id, которую мы определили в строке ранее. Чтобы установить переменную id в качестве ключа, мы должны использовать [ ].
Точно так же вы не можете получить доступ к свойствам с символьными ключами, используя точечный синтаксис. Вы должны использовать квадратные скобки, как указано выше.
Symbol пропускаются обычными функциями проверки объектов
Поскольку Symbol были разработаны для предотвращения конфликтов, символьные свойства не видят их в наиболее распространенных функциях проверки объектов JavaScript, таких как цикл for-in. Символы в качестве ключей свойств также игнорируются в Object.keys(obj) и Object.getOwnPropertyNames(obj).
Также обратите внимание, что свойства символа объекта игнорируются при использовании JSON.stringify().
Глобальный реестр Symbol
Как мы видели выше, Symbol уникальны даже с теми же описаниями, которые мы передаем в качестве параметров. Но могут быть случаи, когда вам понадобится несколько веб-страниц или несколько модулей на одной веб-странице для совместного использования Symbol. В такие моменты вы можете использовать глобальный реестр Symbol — Global Symbol Registry..
Хотя это звучит как сложная система, интерфейс довольно прост в использовании.
Symbol.for(key)
Этот метод ищет существующие символы в глобальном реестре symbol с помощью предоставленного ключа и возвращает его, если он найден. Если он не найден, он создаст symbol с ключом в глобальном реестре symbol и вернет его
Symbol.keyFor(sym)
Этот метод выполняет обратную функцию метода Symbol.for(). Он извлекает общий ключ символа из глобального реестра Symbol для данного символа.
Случаи использования
Скрытые свойства
Предположим, что вы хотите разработать библиотеку для сортировки списка элементов.
Как можно это решить? У вас есть множество способов решить эту проблему. Но при этом вам нужно будет постараться избегать сортировку уже ранее отсортированного списка. Хотя конечным результатом будет так же отсортированный список, повторная сортировка будет неэффективна, поскольку алгоритм реализует фактический процесс сортировки на уже отсортированном массиве.
Вместо того, чтобы реализовывать решение, которое было бы излишним, мы можем просто установить флаг, чтобы указать, был ли список отсортирован или нет. Это легко позволит нам отсортировать наш список только тогда, когда он не отсортирован.
Реализация могла бы выглядеть примерно так.
Хотя приведенная выше реализация отлично справляется со своей задачей, в некоторых аспектах она не будет работать.
В такой ситуации идеально подходят Symbol, так как они избегают столкновений.
Системные Symbol
JavaScript использует несколько внутренних Symbol для точной настройки производительности в различных аспектах. Некоторые из них,
Вы можете прочитать о них здесь.
Примечание: Symbol не полностью скрыты.
Вы по-прежнему можете использовать пользовательские методы, такие как Object.getOwnPropertySymbols(obj) и Reflect.ownKeys(obj), для получения Symbol, используемых в качестве ключей объекта. Вы можете спросить, зачем это. Я лично считаю, что Symbol были созданы, чтобы избежать непреднамеренных конфликтов имен. Если кто-то действительно хотел перезаписать ключ символьного свойства, я думаю, он может это сделать в любом случае.
Популярный вопрос, поднятый программистами React относительно Symbol
Во время обсуждения с редактором Bits and Pieces меня попросили затронуть проблему, поднятую в React JS, связанную с Symbol. Ниже приведена ссылка на данную проблему. Использование Symbol в виде ключей в дочерних элементах при рендеринге массивов или итераторов: Запрашиваемый функционал · Issue № 11996
Желаемый функционал
Для тех, кто не переходил по указанной выше ссылке или не понял, о чем идет речь, ниже я приводу краткое изложение.
Разработчики React должны быть знакомы с концепцией ключей. Ниже приведена выдержка из глоссария документации React (https://reactjs.org/docs/glossary.html#keys).
«Ключ» — это специальный строковый атрибут, который необходимо включать при создании массивов элементов. Ключи помогают React определить, какие элементы были изменены, добавлены или удалены. Ключи должны быть даны элементам внутри массива, чтобы придать элементам стабильную идентичность.
Основная причина необходимости ключей — уникальность объектов. Это необходимо, чтобы иметь возможность однозначно идентифицировать одноуровневые элементы в массиве. Это звучит как отличный вариант использования Symbol, поскольку они уникальны и могут использоваться для уникальной идентификации каждого родственного элемента в массиве.
Но когда вы добавляете Symbol в качестве ключа к элементу массива, вы получите следующую ошибку.
Error Screenshot by Author
Причина указанной выше ошибки заключается в том, что ключи должны быть строкового типа. Если вы помните, что мы ранее рассмотрели, Symbol не относятся к строковому типу и не могут неявно преобразовывать себя в отличие от других примитивных типов данных.
Запрашиваемый функционал состоит в том, чтобы разрешить поддержку Symbol в качестве ключей изначально, поскольку они не преобразуются автоматически в строки.
Почему команда отказалась и закрыла этот вопрос
Дэн Абрамов прокомментировал и закрыл эту проблему, отметив: «Я не вижу практического случая использования Symbol, кроме недоразумения». Он также упоминает, что вы можете просто использовать «идентификатор клиента (customer ID) или имя пользователя» или что-то еще, что связано с данными, которые вы обрабатываете.
Я хотел бы высказать свое мнение с обеих сторон.
Прежде всего, могут быть случаи, когда вы обрабатываете список данных без идентификатора. Это может происходить, когда данные собираются из внешнего интерфейса и отображаются в виде списка. Например простой список номеров. Что делать, если данные вводятся пользователем? Вам нужно будет установить счетчик, чтобы присвоить уникальный ключ каждой записи. Некоторые использовали бы подход присвоения индекса массива каждому элементу, но, как известно, это плохая идея. Вы не можете использовать входное значение в качестве ключа, потому что могут быть повторяющиеся входные данные. Как было предложено, Symbol будет более простой альтернативой.
Но….
В предложении есть что-то в корне неверное. Взгляните на приведенный ниже пример кода.
Как видите, все 4 ключа уникальны. Когда происходит изменение значения элемента, React знает, какой из них изменился, и запускает перестроение. Но когда дерево перестраивается, ключ этого конкретного элемента снова изменится, поскольку Symbol() будет давать уникальное значение каждый раз, когда он вызывается. Ключ будет разным для каждого рендера, что заставит React повторно re-mount элемент/компонент.
Если вам непонятно, как работает процесс построения дерева и обнаружение изменений в приведенном выше сценарии, просмотрите объяснение, данное в документации.
Вы можете избежать этой проблемы повторного рендеринга, используя глобальный реестр symbol — Symbol.for(key), поскольку каждый раз, когда вы вызываете symbol, вы будете искать symbol в глобальном реестре, и если он будет найден, он будет возвращен.
Но опять же….
В этом подходе тоже что-то не так. Чтобы получить Symbol из глобального реестра, вы должны предоставить ключ символа, который сам по себе является уникальным. Если задуматься, этот ключ сам по себе уникален для идентификации каждого элемента. Тогда зачем нам создавать Symbol в этом случае?
Примечание
Но есть решение, предоставленное Eduardo, когда вы инициализируете объект или массив один раз с помощью Symbol, а затем они никогда не инициализируются повторно. Это означает, что значение не будет пересчитываться при каждом рендеринге, и поэтому значения (Symbol) всегда будут одинаковыми. Но этот подход будет работать только в определенных ситуациях.
Обратите внимание, что все указанные решения будут работать, но они вызовут ненужные re-mounts и вызовут нежелательную нагрузку на память и процессор. А наша цель состоит в том, чтобы найти решение с использованием Symbol, которое также будет эффективным.
Если у вас есть какие-либо комментарии, пожалуйста, оставьте их в блоге автора статьи.
Зачем они нужны в JavaScript? Symbol, Iterator, Generator
Jun 1, 2018 · 5 min read
Кратко, просто и понятно, что это такое и как это применять.
Начнём с Symbol
Что это?
Пока всё просто. Теперь давайте посложнее.
Это удерживает разработчиков от создания явного объекта-обёртки Symbol вместо нового символьного значения. Создание явных объектов-обёрток для примитивных типов доступно (например, new Boolean, new String, new Number).
Простыми словами — от символа мы хотим получить уникальный идентификатор, то есть сам Символ, а new возвращает объект, объект нам не нужен.
И последнее про Symbol
Существуют «глобальные символы», они доступны во всех частях вашей программы. То есть вы можете создать символ и поместить его в некую базу, это делается с помощью функции Symbol.for()
Если мы ещё раз вызовем Symbol.for(“Kanye West”) он вернёт существующий символ, а не новый.
Представим, у вас есть некий объект в приложении, который используется много где, да и с ним работаете не только вы, он передаётся по разным функциям, над ним издеваются с помощью разных методов, и вдруг вам понадобилось запихнуть в этот объект свои данные, например логи.
Что вы будете делать — создадите новое поле объекта и присвоите ему свои логи. Но тут есть нюанс, если объект используется в большом количестве логики и другие разработчики как-то взаимодействуют с параметрами объекта, то есть шанс, что ваше новое поле где-то сможет сломать цикл, а где-то перезапишет собой другие поля.
Для решение этой проблемы, можно использовать symbol
Так что, Symbol — ваш друг.
Также есть и более замороченные применения, про них можно почитать тут https://www.keithcirkel.co.uk/metaprogramming-in-es6-symbols/
Iterator
Что это?
В JavaScript есть так называемые итерируемые объекты, то есть объекты содержимое которых мы можем перебрать. Как например массив. Сам перебор, в большинстве своём, осуществляется с помощью итераторов (к примеру конструкция for..of для перебора элементов использует итераторы)
Давайте сделаем один.
А теперь используем наш итератор.
Теперь более реальный пример.
У нас есть объект, который нужно «умно» перебрать.
Для того чтобы for..of выводил то, что мы хотим, нужно сделать объект range итерируемым.
Дальше без паники, всё объясню.
Как я говорил выше, конструкция for..of использует итераторы для перебора данных. В начале своего выполнения, for..of автоматически вызывает Symbol.iterator() для получения итератора и далее вызывает метод next() до получения < done: true >. Это внутренняя механика JavaScript, так уж он работает.
Когда хочешь перебирать объекты и другие типы данных по своему, итератор это отличный вариант.
Generator
Это функция которая может приостанавливать своё выполнение, возвращать промежуточный результат и далее возобновлять своё выполнение в произвольный момент времени. Слегка трудновато, но позже всё станет понятно.
Так выглядят генераторы.
В общем, это обычная функция перед которой стоит *
Когда мы вызываем функцию-генератор, она возвращает нам объект-итератор. Вы с ним уже познакомились.
Вот как это работает на практике
Yield как бы говорит — передаём name и ставим паузу, пока не произойдёт следующий вызов next()
Также yield может принимать значения из вне.
Разберём код выше типичной ситуацией из жизни.
Представим что наш генератор это Канье Уэст который лично привёз вам домой свой альбом. Когда происходит первый вызов myIterator.next() Канье заходит к вам в дом и дарит свой альбом (передается name ). Канье человек простой, он хочет услышать в ответ благодарность и готов ждать её хоть вечность.
Вот, вы послушали альбом, подходите к Канье и говорите Спасибо (с помощью myIterator.next(‘West’) передаётся ‘West’ обратно в функцию), Канье принимает благодарность (переменной who присваивается ‘West’ ) и тут же уходит по своим делам. Такой уж Канье.
В целом, его используют разные библиотеки как замену async/await для работы с асинхронными операциями. Сам async/await кстати, это high level абстракция над генераторами.
Также популярная redux-saga использует генераторы.
В остальных случаях их применяют очень редко.
Суммируем
И напоследок
Сегодня страшное стало понятным….слегка.
Если вы используете symbol, iterator и generator в своих проектах как-то по другому, пишите в комментариях, обязательно добавлю.
Если интересно как надо писать на React, то ниже есть гайд.
На этом всё! Спасибо!
CLAP. CLAP. CLAP.
Потому что, почему бы и нет? Ты можешь. 👏
Exploring JavaScript Symbols. Symbol — новый тип данных в JavaScript
Это первая часть про символы и их использование в JavaScript.
Новая спецификация ECMAScript (ES6) вводит дополнительный тип данных — символ (symbol). Он пополнит список уже доступных примитивных типов (string, number, boolean, null, undefined). Интересной особенностью символа по сравнению с остальными примитивными типами является то, что он единственный тип у которого нет литерала.
Для чего же нужен был дополнительный тип данных?
Такая конструкция объявления всё равно не лишает возможности получить значение свойства, если напрямую обратиться к нему:
В других языках, к примеру, можно добавить модификатор метода, чтобы определить его видимость (protected, private, public). Но в новой спецификации JavaScript выбрали другой подход и решили не вводить модификаторы, а определять поведение в зависимости от типа идентификатора свойства. Раньше имя свойства было строкой, теперь же это может быть как строка так и символ. Такой подход позволяет не менять саму концепцию объявления объектов:
В данном случае будут вычислены все три выражения и их результаты будут именами свойств. Возможность использовать динамические (получаемые в результате вычисления выражения) имена свойств для литералов объекта добавлены в ES6.
Ключевой особенностью символа, которой он отличается от строки, является то, что обратиться к свойству которое объявлено через символ можно только по ссылке на данный символ. К примеру, eсли у объекта user нужно получить имя пользователя нужно написать данный код:
Получить роль пользователя таким образом мы не можем:
Для того, чтобы получить роль, нужно обращаться к свойству по ссылке на символ:
Рассмотрим особенности символов.
Как уже было показано в примере выше, чтобы создать символ нужно вызвать функцию Symbol :
Функция Symbol также принимает необязательный параметр — строку, которая служит для описания символа:
Описание символа служит только для того, чтобы помочь при отладке, оно не изменяет поведение символа и обратиться к символу через описание нельзя, также нет метода, чтобы получить или изменить описание символа.
Спецификация ES6 больше не поддерживает явное создание объектов примитивов, поэтому следующая конструкция выбросит ошибку:
Важной особенностью символа также является то, что его значение уникально:
Это поведение открывает перед нами больше возможностей при работе с объектами, например, несколько модулей могут расширять объект новыми свойствами, не беспокоясь за возможные конфликты имен.
Чтобы не было такой неоднозначности, и было выбрано поведение, что при попытке преобразовать символ будет ошибка.