Что означает символ в javascript
Введение в JavaScript символы
Каждый раз, когда вы создаете новый символ, он уникален. Символы в основном используются для уникальных идентификаторов в объекте. Это единственная цель символа.
Есть также специальные символы, которые мы можем использовать для реализации различных операций или переопределения поведения некоторых операций по умолчанию.
Определение символов
Чтобы создать новые символы, мы можем написать:
Встроенные методы
Существуют встроенные символы, которые используются в качестве идентификаторов для различных методов и значений. Методы с некоторыми символами вызываются, когда используются некоторые операторы.
Symbol.hasInstance
Мы можем переопределить метод Symbol.hasInstance следующим образом:
Symbol.isConcatSpreadable
Мы можем использовать его как в следующем коде:
Первый console.log должен вывести:
А второй должен вывести:
Symbol.iterator
Например, учитывая, что у нас есть следующий код:
Если вы попытаетесь перебрать массив с помощью цикла for. of или функции forEach или попытаться использовать оператор распространения с ним, пример с объектом obj приведет к ошибке, поскольку это не повторяемый объект.
Оператор распространения также будет работать. Если у нас есть следующий код:
Symbol.match
В противном случае они будут считаться объектами регулярного выражения, даже если они являются строками, и мы получим следующую ошибку:
Symbol.replace
Мы можем создать наш собственный метод replace для нашего объекта следующим образом, используя Symbol.replace в качестве идентификатора метода:
У класса Replacer есть конструктор, который принимает значение, которое можно использовать для замены текущего экземпляра строки.
Symbol.search
Symbol.species
Свойство, в качестве значения которого используется функция, являющаяся функцией конструктора, которая используется для создания производных объектов.
Symbol.split
Symbol.toPrimitive
Метод, который преобразует объект в соответствующее примитивное значение. Он вызывается, когда используется унарный оператор + или конвертирует объект в примитивную строку.
Например, мы можем написать наш собственный метод Symbol.toPrimitive для преобразования различных значений в примитивное значение:
Symbol.toString
Symbol.unscopables
Вывод
Особенности использования типа данных Symbol в JavaScript
Символьные примитивы — это одно из новшеств стандарта ES6, которое принесло в JavaScript некоторые ценные возможности. Символы, представленные типом данных Symbol, особенно полезны при использовании их в качестве идентификаторов свойств объектов. В связи с таким сценарием их применения напрашивается вопрос о том, что такого они могут, чего не могут строки.
В материале, перевод которого мы сегодня публикуем, речь пойдёт о типе данных Symbol в JavaScript. Начнём мы с обзора некоторых возможностей JavaScript, в которых нужно ориентироваться для того, чтобы разобраться с символами.
Предварительные сведения
Примитивные значения иммутабельны. Их нельзя изменять. Конечно, в переменную, хранящую примитивное значение, можно записать что-то новое. Например, здесь выполняется запись нового значения в переменную x :
В некоторых языках, например — в C, есть концепции передачи аргументов функций по ссылке и по значению. В JavaScript тоже есть нечто подобное. То, как именно организуется работа с данными, зависит от их типа. Если в функцию передают примитивное значение, представленное некоей переменной, а потом изменяют его в этой функции, значение, хранящееся в исходной переменной, при этом не меняется. Однако если в функцию передать объектное значение, представленное переменной, и модифицировать его, то изменится и то, что хранится в этой переменной.
Рассмотрим следующий пример:
Однако конструирование объектных значений, внешне выглядящих одинаково, не приведёт к тому, что получатся сущности, при сравнении которых будет выявлено их равенство друг другу. Проверить это можно так:
Объекты играют фундаментальную роль в JavaScript. Они применяются буквально повсюду. Например, часто их используют в виде коллекций вида ключ/значение. Но до появления типа данных Symbol в качестве ключей объектов можно было применять лишь строки. В этом крылось серьёзное ограничение использования объектов в виде коллекций. При попытке назначения нестрокового значения в виде ключа объекта это значение приводилось к строке. Убедиться в этом можно так:
Кстати, хотя это немного уводит нас от темы символов, хочется отметить, что структура данных Map была создана для того чтобы позволить использовать хранилища данных формата ключ/значение в ситуациях, когда ключ не является строкой.
Что такое символ?
Теперь, когда мы выяснили особенности примитивных значений в JavaScript, мы наконец готовы к тому, чтобы приступить к разговору о символах. Символ — это уникальное примитивное значение. Если подходить к символам с этой позиции, то можно заметить, что символы в этом плане похожи на объекты, так как создание нескольких экземпляров символов приведёт к созданию разных значений. Но символы, кроме того, являются иммутабельными примитивными значениями. Вот пример работы с символами:
При создании экземпляра символа можно воспользоваться необязательным первым строковым аргументом. Этот аргумент представляет собой описание символа, которое предназначено для использования при отладке. На сам символ это значение не влияет.
Символы как ключи свойств объектов
Символы можно использовать в качестве ключей свойств объектов. Это очень важно. Вот пример использования их в таком качестве:
На первый взгляд может показаться, что вышеописанные особенности символов позволяют использовать их для создания приватных свойств JS-объектов. Во многих других языках программирования можно создавать скрытые свойства объектов с использованием классов. Отсутствие этой возможности уже давно считается одним из недостатков JavaScript.
К сожалению, код, который работает с объектами, может беспрепятственно обращаться к их строковым ключам. Код может обращаться и к ключам, заданным символами, причём, даже в том случае, если у кода, из которого работают с объектом, нет доступа к соответствующему символу. Например, с помощью метода Reflect.ownKeys() можно получить список всех ключей объекта, и тех, что являются строками, и тех, что являются символами:
Обратите внимание на то, что в настоящее время ведётся работа над тем, чтобы оснастить классы возможностью использования приватных свойств. Эта возможность называется Private Fields (приватные поля). Она, правда, не затрагивает абсолютно все объекты, относясь лишь к тем из них, которые созданы на основе предварительно подготовленных классов. Поддержка приватных полей уже имеется в браузере Chrome версии 72 и старше.
Предотвращение коллизий имён свойств объектов
Символы, конечно, не добавляют в JavaScript возможностей по созданию приватных свойств объектов, но они являются ценным новшеством языка по другим причинам. А именно, они полезны в ситуациях, когда неким библиотекам нужно добавлять свойства в объекты, описанные за их пределами, и при этом не опасаться коллизии имён свойств объектов.
Если же воспользоваться в нашем примере символами, то каждая библиотека может сгенерировать, при инициализации, нужные ей символы. Затем эти символы могут быть использованы для назначения свойств объектам и для доступа к этим свойствам.
Именно глядя на подобный сценарий можно ощутить пользу от появления символов в JavaScript.
Однако тут может возникнуть вопрос, касающийся использования библиотеками, для имён свойств объектов, случайных строк или строк, со сложной структурой, включающих в себя, например, название библиотеки. Подобные строки могут образовывать нечто вроде пространств имён для идентификаторов, используемых библиотеками. Например, это может выглядеть так:
В общем-то, можно поступить и так. Подобные подходы, на самом деле, очень похожи на то, что происходит при использовании символов. И если, используя случайные идентификаторы или пространства имён, пара библиотек не сгенерирует, по воле случая, одинаковые имена свойств, то проблем с именами не будет.
Проницательный читатель сказал бы сейчас, что два рассматриваемых подхода к именованию свойств объектов не являются полностью эквивалентными. У имён свойств, которые формируются случайным образом или с использованием пространств имён, есть недостаток: соответствующие ключи очень легко обнаружить, особенно если в коде выполняется перебор ключей объектов или их сериализация. Рассмотрим следующий пример:
Если бы в этой ситуации для имени ключа использовался бы символ, тогда JSON-представление объекта не содержало бы значения символа. Почему это так? Дело в том, что то, что в JavaScript появился новый тип данных, ещё не означает того, что изменения внесены и в спецификацию JSON. JSON поддерживает, в качестве ключей свойств объектов, только строки. При сериализации объекта не делается попыток представить символы в каком-то особом виде.
Рассматриваемую проблему попадания имён свойств в JSON-представление объектов можно решить благодаря использованию Object.defineProperty() :
Однако между использованием имён-символов и имён, созданных с использованием других механизмов, есть одно маленькое различие. Так как строки иммутабельны, а символы гарантированно уникальны, всегда есть возможность того, что кто-то, перебрав все возможные сочетания символов в строке, вызовет коллизию имён. С математической точки зрения это означает, что символы действительно дают нам ценную возможность, которая отсутствует у строк.
Имитация приватных свойств
Вот интересный подход, который можно использовать для имитации приватных свойств объектов. Этот подход предусматривает применение ещё одной современной возможности JavaScript — прокси-объектов. Такие объекты служат обёртками для других объектов, которые позволяют программисту вмешиваться в действия, выполняемые с этими объектами.
Прокси-объекты предлагают много способов для перехвата действий, выполняемых над объектами. Нас интересует возможность управления операциями чтения ключей объекта. В подробности о прокси-объектах мы тут углубляться не будем. Если вам они интересны — взгляните на эту публикацию.
Тут надо отметить, что в Node.js есть одна особенность, нарушающая приватность прокси-объектов. Эта особенность не существует в самом языке, поэтому она не актуальна для других сред выполнения JS, таких, как браузер. Дело в том, что эта особенность позволяет получать доступ к объекту, скрытому за прокси-объектом, при наличии доступа к прокси-объекту. Вот пример, демонстрирующий возможность обхода механизмов, показанных в предыдущем фрагменте кода:
Итоги
Уважаемые читатели! Пользуетесь ли вы символами в своих JavaScript-проектах?
Выражения и операторы
Эта глава описывает выражения и операторы языка JavaScript, такие как операторы присваивания, сравнения, арифметические, битовые, логические, строчные, и различные специальные операторы.
Полный и детальный список операторов и выражений также доступен в этом руководстве.
Операторы
В JavaScript есть следующие типы операторов. Данный подраздел описывает каждый тип и содержит информацию об их приоритетах друг над другом.
В свою очередь унарная операция использует один операнд, перед или после оператора:
Операторы присваивания
Существуют также составные операторы присваивания, которые используются для сокращённого представления операций, описанных в следующей таблице:
Деструктуризация
Операторы сравнения
Замечание: (=>) не оператор, а нотация Стрелочных функций.
Арифметические операторы
Арифметические операторы (en-US) используют в качестве своих операндов числа (также литералы или переменные) и в качестве результата возвращают одно числовое значение. Стандартными арифметическими операторами являются сложение (+), вычитание (-), умножение (*), и деление (/). При работе с числами с плавающей точкой эти операторы работают аналогично их работе в большинстве других языках программирования (обратите внимание, что деление на ноль возвращает бесконечность Infinity ). Например:
Кроме того, JavaScript позволяет использовать следующие арифметические операторы, представленные в таблице:
Битовые (поразрядные) операторы
Битовые операторы (en-US) обрабатывают свои операнды как последовательности из 32 бит (нулей и единиц), а не как десятичные, шестнадцатеричные или восьмеричные числа. Например, десятичное число 9 имеет двоичное представление 1001. Битовые операторы выполняют операции над таким двоичным представлением, но результат возвращают как обычное числовое значение JavaScript.
Следующая таблица обобщает битовые операторы JavaScript.
Оператор | Использование | Описание |
---|---|---|
Побитовое И (en-US) | a & b | Возвращает единицу в каждой битовой позиции, для которой соответствующие биты обеих операндов являются единицами. |
Побитовое ИЛИ (en-US) | a | b | Возвращает единицу в каждой битовой позиции, для которой один из соответствующих битов или оба бита операндов являются единицами. |
Исключающее ИЛИ (en-US) | a ^ b | Возвращает единицу в каждой битовой позиции, для которой только один из соответствующих битов операндов является единицей. |
Побитовое НЕ (en-US) | Заменяет биты операнда на противоположные. | |
Сдвиг влево (en-US) | a | Сдвигает a в двоичном представлении на b бит влево, добавляя справа нули. |
Сдвиг вправо с переносом знака (en-US) | a >> b | Сдвигает a в двоичном представлении на b бит вправо, отбрасывая сдвигаемые биты. |
Сдвиг вправо с заполнением нулями (en-US) | a >>> b | Сдвигает a в двоичном представлении на b бит вправо, отбрасывая сдвигаемые биты и добавляя слева нули. |
Битовые логические операторы
Основной смысл работы битовых логических операторов состоит в следующем:
Выражение | Результат | Двоичное описание | |||||
---|---|---|---|---|---|---|---|
15 & 9 | 9 | 1111 & 1001 = 1001 | |||||
15 | 9 | 15 | 1111 | 1001 = 1111 | |||||
15 ^ 9 | 6 | 1111 ^ 1001 = 0110 | |||||
Тип оператора | Операторы |
---|---|
свойство объекта | . [] |
вызов, создание экземпляра объекта | () new |
отрицание, инкремент | ! Более подробная версия данной таблицы, содержащая ссылки и дополнительную информацию по каждому оператору, находится в справочнике JavaScript. ВыраженияВыражением является любой корректный блок кода, который возвращает значение. Концептуально, существуют два типа выражений: те которые присваивают переменной значение, и те, которые вычисляют значение без его присваивания. Код 3 + 4 является примером выражения второго типа. Данное выражение использует оператор «+» для сложения чисел 3 и 4 без присваивания переменной полученного результата 7. Все выражения в JavaScript делятся на следующие категории: Основные выраженияБазовые ключевые слова и основные выражения в JavaScript. Оператор thisИспользуйте ключевое слово this для указания на текущий объект. В общем случае this указывает на вызываемый объект, которому принадлежит данный метод. Используйте this следующим образом: Предположим, функция validate выполняет проверку свойства value некоторого объекта; задан объект, а также верхняя и нижняя граница величины данного свойства: Вы можете вызвать функцию validate для обработчика события onChange для каждого элемента формы, используя this для указания на элемент формы, как это показано в следующем примере: Оператор группировкиУпрощённый синтаксис создания массивов и генераторов[for (x of y) x] Упрощённый синтаксис для массивов. (for (x of y) y) Упрощённый синтаксис для генераторов. Упрощённые синтаксисы существуют во многих языках программирования и позволяют вам быстро собирать новый массив, основанный на существующем. Например: Левосторонние выраженияЗначениям слева назначаются значения справа. Вы можете использовать оператор new для создания экземпляра объекта пользовательского типа или одного из встроенных объектов. Используйте оператор new следующим образом: superКлючевое слово используется, чтобы вызывать функции родительского объекта. Это полезно и с классами для вызова конструктора родителя, например. Оператор расширенияОператор расширения позволяет выражению расширяться в местах с множеством аргументов (для вызовов функций) или множестве элементов (для массивов). Похожим образом оператор работает с вызовами функций: Exploring JavaScript Symbols. Использование символовЭто вторая статья из серии про символы и их использование в JavaScript. Предыдущая часть: «Symbol — новый тип данных в JavaScript». С появлением символов объект Object был расширен одним методом, который позволяет получить все символы объекта: Наличие этого метода лишает нас возможности создавать по-настоящему приватные свойства. Вот, например, как можно получить роль пользователя: Хочу отметить также, что спецификация ECMAScript 6 добавила новый объект Reflect который дает возможность рефлексии (или отражения) в JavaScript. И один из методов объекта Reflect позволяет получить все свойства, которые объявленные и через строки, и через символы. Символы дают возможность для разделения свойств на публичные и внутренние, которые хранят внутреннюю информацию и используются только в процессе обращения к публичным. Но все же основной причиной добавления символов является не необходимость создания приватных свойств (такая возможность, кстати, также была предложена — private name objects, но она по ряде причин не вошла в текущую версию спецификации). Основная проблема, которую будут решать символы, это создание уникальных свойств. Разработчики библиотек с помощью символов могут добавлять свойства которые гарантированно будут уникальными, а это значит, что не будут конфликтовать с другими свойствами (они не переопределят свойства, которые добавлены другими модулями, также пользовательский код не сможет случайно переопределить данные свойства). В этом примере также предоставлен еще один способ использования символов. Объект states можно рассматривать как перечисляемый тип с уникальными значениями (что позволяет проверить или передано корректное значение). Теперь давайте рассмотрим методы, которые позволяют создавать символы в глобальном реестре символов. Для решения этой проблемы был введен глобальный реестр для символов. Чтобы создать символ который будет доступен во всех контекстах нужно воспользоваться методом Symbol.for : Этот метод также используется для получения символа из глобального реестра. Логика работы метода следующая: если символ найден, тогда он будет возвращен как результат работы метода; если символ не найден в реестре, он будет создан, добавлен в глобальный реестр и возвращен как результат. Можно увидеть, что Array в текущем контексте не равен Array в другом контексте (каждый имеет свою копию). Аналогичное поведение как и в ситуации с iframe и со символами, они не равны друг другу в разных контекстах. Объявив символы через Symbol.for мы получаем возможность использовать их во всех контекстах. Если в вашем проекте используется модуль vm с исполнением кода с определенными контекстами, использованик Symbol.for будет отличным решением в данной ситуации. Есть также метод, который позволяет получить ключ, по которому символ добавлен в реестр — Symbol.keyFor : Хочу еще раз отметить, что простое создание символа через Symbol(‘score’) — не создат символ в глобальном реестре.
|