Статьи

ES6 в действии: символы и их использование

В то время как ES2015 представил много языковых функций, которые были в списке пожеланий разработчиков в течение некоторого времени, есть некоторые новые функции, которые менее известны и понятны, и преимущества которых намного менее очевидны, такие как символы.

Символ — это новый примитивный тип, уникальный токен, который гарантированно никогда не столкнется с другим символом. В этом смысле вы можете рассматривать символы как некий UUID (универсальный уникальный идентификатор). Давайте посмотрим, как работают символы, и что мы можем с ними сделать.

Создание новых символов

Создать новые символы очень просто и это просто случай вызова функции Symbol . Обратите внимание, что это просто стандартная функция, а не конструктор объекта. Попытка вызвать его с newTypeError Каждый раз, когда вы вызываете функцию Symbol

 const foo = Symbol();
const bar = Symbol();

foo === bar
// <-- false

Символы также могут быть созданы с меткой, передав строку в качестве первого аргумента. Метка не влияет на значение символа, но полезна для отладки и отображается, если вызывается метод toString() Можно создать несколько символов с одинаковой меткой, но это не дает никаких преимуществ, и это, вероятно, приведет к путанице.

 let foo = Symbol('baz');
let bar = Symbol('baz');

foo === bar
// <-- false
console.log(foo);
// <-- Symbol(baz)

Что я могу сделать с символами?

Символы могут быть хорошей заменой строк или целых чисел как констант класса / модуля:

 class Application {
  constructor(mode) {
    switch (mode) {
      case Application.DEV:
        // Set up app for development environment
        break;
      case Application.PROD:
        // Set up app for production environment
        break;
      case default:
        throw new Error('Invalid application mode: ' + mode);
    }
  }
}

Application.DEV = Symbol('dev');
Application.PROD = Symbol('prod');

// Example use
const app = new Application(Application.DEV);

Строка и целые числа не являются уникальными значениями; такие значения, как число 2development Использование символов означает, что мы можем быть более уверенными в отношении предоставляемого значения.

Другое интересное использование символов в качестве ключей свойств объекта. Если вы когда-либо использовали объект JavaScript в качестве хеш-карты (ассоциативный массив в терминах PHP или словарь в Python), вы будете знакомы с получением / настройкой свойств с помощью скобочной записи:

 const data = [];

data['name'] = 'Ted Mosby';
data['nickname'] = 'Teddy Westside';
data['city'] = 'New York';

Используя обозначение в скобках, мы также можем использовать символ в качестве ключа свойства. Есть несколько преимуществ для этого. Во-первых, вы можете быть уверены, что символьные ключи никогда не будут конфликтовать, в отличие от строковых ключей, которые могут конфликтовать с ключами для существующих свойств или методов объекта. Во-вторых, они не будут перечислены в циклах for … inObject.keys()Object.getOwnPropertyNames()JSON.stringify() Это делает их идеальными для свойств, которые вы не хотите включать при сериализации объекта.

 const user = {};
const email = Symbol();

user.name = 'Fred';
user.age = 30;
user[email] = '[email protected]';

Object.keys(user);
// <-- Array [ "name", "age" ]

Object.getOwnPropertyNames(user);
// <-- Array [ "name", "age" ]

JSON.stringify(user);
// <-- "{"name":"Fred","age":30}"

Однако стоит отметить, что использование символов в качестве ключей не гарантирует конфиденциальность. Предусмотрено несколько новых инструментов, позволяющих получить доступ к символьным ключам свойств. Object.getOwnPropertySymbols()Reflect.ownKeys()

 Object.getOwnPropertySymbols(user);
// <-- Array [ Symbol() ]

Reflect.ownKeys(user)
// <-- Array [ "name", "age", Symbol() ]

Знаменитые символы

Поскольку свойства с символьными ключами фактически невидимы для кода до ES6, они идеально подходят для добавления новых функций в существующие типы JavaScript без нарушения обратной совместимости. Так называемые «общеизвестные» символы — это предопределенные свойства функции Symbol

Symbol.iterator

 const band = ['Freddy', 'Brian', 'John', 'Roger'];
const iterator = band[Symbol.iterator]();

iterator.next().value;
// <-- { value: "Freddy", done: false }
iterator.next().value;
// <-- { value: "Brian", done: false }
iterator.next().value;
// <-- { value: "John", done: false }
iterator.next().value;
// <-- { value: "Roger", done: false }
iterator.next().value;
// <-- { value: undefined, done: true }

Все встроенные типы StringArrayTypedArrayMapSetSymbol.iteratorfor … of Браузеры также начинают использовать ключ Symbol.iteratorNodeListHTMLCollection

Глобальный реестр

Спецификация также определяет реестр символов во время выполнения, что означает, что вы можете хранить и извлекать символы в разных контекстах выполнения, например, между документом и встроенным iframe или сервисным работником.

Symbol.for(key) Если для ключа не существует символа, возвращается новый. Как и следовало ожидать, последующие вызовы для того же ключа вернут тот же символ.

Symbol.keyFor(symbol) Вызов метода с символом, которого нет в реестре, возвращает undefined:

 const debbie = Symbol.for('user');
const mike   = Symbol.for('user');

debbie === mike
// <-- true

Symbol.keyFor(debbie);
// <-- "user"

Случаи использования

Есть несколько случаев, когда использование символов дает преимущество. Одна из них, о которой я говорил ранее в этой статье, — это когда вы хотите добавить «скрытые» свойства к объектам, которые не будут включены при сериализации объекта.

Авторы библиотек также могут использовать символы для безопасного дополнения клиентских объектов свойствами или методами, не беспокоясь о перезаписи существующих ключей (или о перезаписи их ключей другим кодом). Например, компоненты виджета (такие как средства выбора даты) часто инициализируются с различными параметрами и состоянием, которое необходимо где-то хранить. Присвоение экземпляра виджета свойству объекта элемента DOM не является идеальным, поскольку это свойство может потенциально конфликтовать с другим ключом. Использование ключа на основе символов аккуратно обходит эту проблему и гарантирует, что ваш экземпляр виджета не будет перезаписан. См. Сообщение в блоге Mozilla Hacks ES6 in Depth: Symbols для более подробного изучения этой идеи.

Поддержка браузера

Если вы хотите поэкспериментировать с символами, поддержка основного браузера достаточно хороша . Как видите, текущие версии Chrome, Firefox, Microsoft Edge и Opera изначально поддерживают тип Symbol, наряду с Android 5.1 и iOS 9 на мобильных устройствах. Также доступны полифиллы, если вам нужна поддержка старых браузеров.

Вывод

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