В то время как ES2015 представил много языковых функций, которые были в списке пожеланий разработчиков в течение некоторого времени, есть некоторые новые функции, которые менее известны и понятны, и преимущества которых намного менее очевидны, такие как символы.
Символ — это новый примитивный тип, уникальный токен, который гарантированно никогда не столкнется с другим символом. В этом смысле вы можете рассматривать символы как некий UUID (универсальный уникальный идентификатор). Давайте посмотрим, как работают символы, и что мы можем с ними сделать.
Создание новых символов
Создать новые символы очень просто и это просто случай вызова функции Symbol . Обратите внимание, что это просто стандартная функция, а не конструктор объекта. Попытка вызвать его с new
TypeError
Каждый раз, когда вы вызываете функцию 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);
Строка и целые числа не являются уникальными значениями; такие значения, как число 2
development
Использование символов означает, что мы можем быть более уверенными в отношении предоставляемого значения.
Другое интересное использование символов в качестве ключей свойств объекта. Если вы когда-либо использовали объект JavaScript в качестве хеш-карты (ассоциативный массив в терминах PHP или словарь в Python), вы будете знакомы с получением / настройкой свойств с помощью скобочной записи:
const data = [];
data['name'] = 'Ted Mosby';
data['nickname'] = 'Teddy Westside';
data['city'] = 'New York';
Используя обозначение в скобках, мы также можем использовать символ в качестве ключа свойства. Есть несколько преимуществ для этого. Во-первых, вы можете быть уверены, что символьные ключи никогда не будут конфликтовать, в отличие от строковых ключей, которые могут конфликтовать с ключами для существующих свойств или методов объекта. Во-вторых, они не будут перечислены в циклах for … in
Object.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 }
Все встроенные типы String
Array
TypedArray
Map
Set
Symbol.iterator
for … of
Браузеры также начинают использовать ключ Symbol.iterator
NodeList
HTMLCollection
Глобальный реестр
Спецификация также определяет реестр символов во время выполнения, что означает, что вы можете хранить и извлекать символы в разных контекстах выполнения, например, между документом и встроенным 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 на мобильных устройствах. Также доступны полифиллы, если вам нужна поддержка старых браузеров.
Вывод
Хотя основная причина введения символов, по-видимому, заключалась в том, чтобы облегчить добавление новых функциональных возможностей в язык без нарушения существующего кода, у них действительно есть несколько интересных применений. Все разработчики должны иметь хотя бы базовые знания о них и быть знакомыми с наиболее часто используемыми, общеизвестными символами и их назначением.