Введение в тип символа, представленный ES2015

Среди многочисленных новинок, которые принес ES6 (также известный как ECMAScript 2015), есть одна, которая, я бы сказал, осталась незамеченной, особенно если мы сравним ее популярность с другими функциями, такими как оператор распространения или шаблоны с тегами, о которых я уже говорил в других статьях:





Как следует из названия, я имею в виду определение нового типа символа, который присоединяется к остальным примитивным типам: string, number, boolean, null и undefined. Итак, в этой статье я собираюсь перечислить основные характеристики этого нового типа, а также его основные применения.

Посмотрим на них!

Тип Символ

Прежде всего, чтобы понять тип символа, нужно обратиться к определению, сделанному для него в документации Mozilla:

Тип данных символ является примитивным типом данных. Функция Symbol() возвращает значение типа symbol, имеет статические свойства, которые раскрывают несколько членов встроенных объектов, имеет статические методы, открывающие глобальный реестр символов, и напоминает встроенный класс объектов, но является неполным как конструктор, поскольку не поддерживает синтаксис new Symbol().

Честно говоря, когда я впервые читал это определение, правда в том, что я остался таким же, как был, поэтому я добавлю одно, которое даст нам лучшее представление о том, что такое тип символа на самом деле:

Тип Symbol позволяет нам получать значения, которые невозможно воссоздать заново, то есть они являются уникальными и неизменяемыми идентификаторами.

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

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

Точно так же, как мы можем создавать примитивные типы, используя фабричные функции (Boolean(false)), способ, которым мы будем создавать значения типа Symbol, будет точно таким же:

const myFirstSymbol = Symbol();

Хотя мы также можем передать string для его создания:

const foo = Symbol('foo');

это не имеет большего смысла, чем возможность идентифицировать этот символ при отладке кода, поскольку при его выводе на экран мы будем иметь:

console.log(foo); // Symbol(foo);

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

Например:

Symbol() === Symbol() // false

Или например:

const a = Symbol('a');
const otherA = Symbol('a');
a === otherA // false

Характеристики типа Символ

Среди основных типов символов можно отметить следующие:

  • Все значения типа Symbol, созданные фабрикой functionSymbol(), уникальны, поэтому они никогда не будут конфликтовать друг с другом.
  • Свойства объекта, ключ которого является символом, не перечисляются с использованием функций Object.getOwnPropertyNames() или Object.keys() или в циклах типа for...of или for...in.
  • Если мы хотим перечислить свойства объекта, ключ которого является символом, мы должны использовать функцию ES6 Object.getOwnPropertySymbols().
  • Значения типа символа не «страдают» приведением типов, то есть следующий код выдаст ошибку:
const foo = Symbol('foo');
console.log('This is the symbol foo: ' + foo); // Error

Доступ к ранее созданным символам

Хотя это правда, что каждый раз, когда мы используем функцию Symbol(), мы получаем уникальный идентификатор, Javascript предоставляет нам метод Symbol.for(key) для создания символа, связанного с переданным ключом, или извлечения его из реестра Символы

Благодаря этой функции, если, например, мы создадим символ, назначив ему ключ:

const foo = Symbol.for('foo');

Позже мы сможем восстановить этот символ следующим образом:

const bar = Symbol.for('foo');
foo === bar; // true

❗❗❗ Будьте осторожны. Чтобы получить такой же символ, необходимо создать его с помощью функции Symbol.for, чтобы следующий код привел к false:

const foo = Symbol('foo');
const bar = Symbol.for('foo');
foo === bar; // false

Предопределенные символы

Javascript определил несколько символов внутри класса Symbol, которые используются другими типами для предоставления различных функций, которые были недоступны разработчикам до появления ES6.

Вероятно, наиболее известным является Symbol.iterator, который позволяет нам получить доступ к итератору по умолчанию объекта:

const arr = [1, 2, 3];
const iterator = arr[Symbol.iterator]();
console.log(iterator.next()); // {value: 'a', done: false}

Варианты использования для типа символа

Что ж, теперь, когда мы знаем, как создавать символы, давайте посмотрим на некоторые из его основных применений.

Символы как ключи свойств объекта

Как упоминается в документации Mozilla, наиболее характерное использование символа - это использование в качестве идентификаторов для свойств объектов:

const foo = Symbol();
const myObject = {};
myObject[foo] = 'foo';
myObject['bar'] = 'bar';
console.log(myObject); // {bar: "bar", Symbol(): "foo"}
console.log(foo in myObject); // true
console.log(myObject[foo]); // 'foo'
console.log(Object.getOwnPropertyNames(myObject)); // ['bar']
console.log(Object.keys(myObject)); ['bar']

Поскольку методы Object.getOwnPropertyNames и Object.keys не возвращают ключи, объявленные с помощью символов, может показаться, что другое их использование может заключаться в том, чтобы позволить нам объявлять частные свойства внутри объектов; поскольку когда-то использовался Symbol() для свойства не было бы возможности восстановить его.

Однако есть альтернативы предыдущим методам для перечисления свойств объектов. Например, метод Reflect.ownKeys() объекта Reflect object способен перечислять свойства, объявленные символами:

console.log(Reflect.ownKeys(myObject)); ['bar', Symbol]

Предотвращение конфликтов имен свойств

Еще одна особенность, охватываемая символом, - это возможность того, что разные разработчики или библиотеки добавляют свойства к объектам, не изменяя существующие.

Например, я могу думать об этой проблеме с 2009 года, когда различные библиотеки перезаписывали объект jQuery, что приводило к множеству сбоев и ошибок. Благодаря Символу это очень просто:

// Library A
const jQuerySymbolA = Symbol('library a');
window[jQuerySymbolA] = $;
// Library B
const jQuerySymbolB = Symbol('library b');
window[jQuerySymbolB] = $;

Таким образом мы гарантируем, что объект jQuery не будет перезаписан другими библиотеками и что мы всегда будем иметь доступ к нашим.

Последние мысли

Я надеюсь, что в этой статье я суммировал наиболее важные характеристики и варианты использования символа. Конечно, это только верхушка айсберга, поэтому отныне наша задача - ознакомиться с его использованием или найти новые способы воспользоваться всем, что они нам предлагают.

использованная литература







Вы хотите прочитать больше подобных статей?

Если вам понравилась эта статья, я призываю вас подписаться на информационный бюллетень, который я отправляю каждое воскресенье с публикациями, похожими на этот и другие рекомендуемые материалы: 👇👇👇