Новая структура данных Symbol – это новый примитивный тип. Он также немутируемый (immutable).
Основная задача Символа – вернуть уникальное значение.
Частиный перевод статей “Deep dive into ES6 Symbols” и “A practical guide to ES6 Symbol” с сайта medium.com.
Синтаксис
Чтобы создать новый символ достаточно использовать функцию конструктор Symbol().
var symA = Symbol(); var symB = Symbol("B"); var symC = Symbol("B");
Все символы выше – уникальны.
symB === symC; // false
Если нужно получить доступ к символу из любого места в коде, нужно использовать метод Symbol.for().
// Символ создается в глобальном реестре const symbol = Symbol.for('the-symbol'); // Где-то в другом месте const sym = Symbol.for('the-symbol'); // Символ получен из глобального реестра // Это один и тот же символ console.log(symbol === sym); // true // Можно получить доступ к описанию символа console.log(Symbol.keyFor(sym)); // the-symbol
Символ можно определить по типу используя typeof
.
let symbol = Symbol(); typeof symbol; // symbol
Всего выделяют три вида Символов:
- Пользовательские Символы – созданные с помощью функции конструктора Symbol();
- Глобальные Символы – созданные с помощью метода Symbol.for();
- Зарезервированные Символы – определенные как статические свойства объекта Symbol.
Для чего используются Символы
Самый распрастраненный случай использования Символов это использование их в качестве свойств объектов. Преимущества Символов перед обычными свойствами в том, что они не видны в списке свойств при итерировании, что делает их отличным инструментом для метапрограммирования и сокрытия внутренних методов и объектов.
Также, Символы используются JS движками для реализации различных фичей. Например, Symbol.iterator
используется для реализации Iterable объектов. Symbol.toPrimitive
используется во время преобразования объектов в примитивные типы. Примеры рассмотрим далее в этой статье.
Список всех стандартных Символов:
// Iteration symbols Symbol.iterator Symbol.asyncIterator // RegEx symbols Symbol.match Symbol.matchAll Symbol.replace Symbol.search Symbol.split // Other symbols Symbol.hasInstance Symbol.isConcatSpreadable Symbol.unscopables Symbol.species Symbol.toPrimitive Symbol.toStringTag
Примеры использования
Символ как свойство объекта:
const s = Symbol('secret'); let data = {}; data[s] = 1; let newData = Object.assign({}, data); Object.getOwnPropertyNames(data); // [] ( s in data ); // true ( s in newData ); // true ( Symbol('secret') in data ); // false !!!
Можно реализовывать нечто вроде перегрузки или переопределения стандартных фичей:
const MATCH = RegExp.prototype[Symbol.match]; RegExp.prototype[Symbol.match] = function(searchString) { return !!MATCH.call(this, searchString) // return true or false } "foo".match(/foo/) // true "foo".match(/bar/) // false
Стандартные символы
Как пример возьмем Класс реализовывающий объект для работы с паролем.
const PWD = Symbol('pwd') const LEN = Symbol('len') const hashCode = str => Array.from(str) .reduce((h, c) => Math.imul(31, h) + c.charCodeAt(0) | 0, 0) class Password { constructor(pwdStr) { this[PWD] = hashCode(pwdStr) this[LEN] = pwdStr.length } [Symbol.toPrimitive](hint) { return '*'.repeat(this[LEN]) } [Symbol.match](pwdStr) { return this[PWD] === hashCode(pwdStr) } get [Symbol.toStringTag]() { return 'Password'; } static [Symbol.hasInstance](instance) { return typeof instance === 'string'; } [Symbol.iterator]() { return ('' + this)[Symbol.iterator]() } } let securePassword = new Password('password'); securePassword.length // 8 ('' + securePassword) // "********" [...securePassword] // ["*","*","*","*","*","*","*","*"] 'password'.match(securePassword) // true 'notmypassword'.match(securePassword) // false securePassword.toString() // "[object Password]"