Новый тип данных ES6 Symbol

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

Как правило он используется при работе с объектами, когда нам необходимо создать уникальное свойство не доступное в открытом виде (скрытое).

Пример создания:

 let sym = Symbol("myname"); 

Важно обратить внимание, что используется не new Symbol, а просто Symbol. Дело в том, что это не конструктор и использование оператора new невозможно.

Так же при объявлении, мы использовали строковый параметр myname, который не обязателен. Он используется для отладки и по нему нельзя обратиться.

Для более полного понимания давайте рассмотрим принцип работы с Symbol в JavaScript на примере.

Для начала создадим новый объект:

 let obj = {
     name: "Вася",
     surename: "Васечкин",
     age: 27
 }

Теперь, что бы обратиться к свойству name мы можем воспользоваться одним и следующих методов:

 console.log(obj.name) // "Вася"
 //или
 console.log(obj["name"]) // "Вася"

Но, что если нам это свойство необходимо использовать как системное и мы не хотим, что бы оно было доступно в открытом виде.

И тут на помощь приходит новый тип данных symbol

 //создаем символ в реестре
 let name = Symbol();

 let obj = {
       [name]: "Вася",
       surename: "Васечкин",
       age: 27,
   }

Особенность такой записи, дает нам возможность запретить обращение к свойству name через строковый литерал. И если мы попытаемся обратиться к нему как в ранее приведенных примерах, то вместо значения «Вася», у нас будет выведено в консоль undefined

 console.log(obj.name) // "undefined"
 //или
 console.log(obj["name"]) // "undefined"

Но если мы к нему обратимся по ранее созданному символу name, то получим необходимое значение

 console.log(obj[name]) // "Вася"

Тем самым мы скрыли это свойство и сделали его уникальным по значению именно символа.

Так же это свойство не будет доступно в цикле перечисления объекта

 for (let key in obj) {
     console.log(key);
 }
 //будут выведены только ключи age и surename. Ключ-символ name будет пропущен

Также его не будет видно через методы Object.keys() и Object.getOwnPropertyNames()

 console.log( Object.keys(obj)); // [ "surename", "age"]
 console.log( Object.getOwnPropertyNames(obj)); // [ "surename", "age"]

Что бы получить символьные свойства объекта, можно воспользоваться методом Object.getOwnPropertySymbols()

 console.log( Object.getOwnPropertySymbols(obj)); // [Symbol()]

Из этого можно сделать вывод, что символьные свойства не являются скрытыми, так как их всегда можно увидеть с помощью метода Object.getOwnPropertySymbols()

Реестр символов

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

 let s = Symbol.for("some symbol");

Он проверяет наличие данного символа с этим описанием в реестре, если такой имеется, то он возвращает его, в противном случае создает.

Получить ключ описание зарегистрированного символа можно используя метод Symbol.keyFor()

 let key = Symbol.keyFor(s);

     console.log(key); //"some symbol"

Метод Symbol.keyFor() работает только с глобальными символами, если при работе с ним используется не глобально созданный символ, то значение будет навно undefined

Важно понимать, что что значение символа уникально

 var a = Symbol('name');
 var b = Symbol('name');

 console.log(a === b); //false

Это позволяет нам не беспокоится за возможные конфликты имен. И если нам все таки нужен один и тот же символ в разных местах кода, то его можно объявить один раз в глобальном реестре символов.

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