Символ

Примитивный тип, представляющий собой уникальный идентификатор. Используется как имя свойств в объектах.

Время чтения: меньше 5 мин

Кратко

Скопировано

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

Символы могут использоваться в качестве имён свойств в объектах. Символьные свойства могут быть прочитаны только при прямом обращении и не видны при обычных операциях.

Как пишется

Скопировано

Создание

Скопировано

Для создания символа нужно вызвать функцию Symbol:

        
          
          const sym = Symbol()const symTwo = Symbol()console.log(sym === symTwo)// false
          const sym = Symbol()
const symTwo = Symbol()

console.log(sym === symTwo)
// false

        
        
          
        
      

При создании символа первым аргументом можно передать его описание. Оно ни на что не влияет и необходимо только для отладки. Даже если описания символов совпадают, JavaScript всё равно создаёт уникальные символы:

        
          
          const sym = Symbol('name')const symTwo = Symbol('name')console.log(sym === symTwo)// false
          const sym = Symbol('name')
const symTwo = Symbol('name')

console.log(sym === symTwo)
// false

        
        
          
        
      

Использование

Скопировано

Символы используются для создания скрытых свойств объектов. В отличие от свойств, ключом которых является строка, символьные свойства может читать только владелец символа. Скрытые свойства не видны при его обходе с помощью for...in:

        
          
          const secondaryId = Symbol()const user = {  'id': 193,  'name': 'Ольга',  [secondaryId]: 'olga-1'}for (const prop in user) {  console.log(prop, user[prop])}// id 193// name Ольгаconsole.log(user[secondaryId])// olga-1
          const secondaryId = Symbol()

const user = {
  'id': 193,
  'name': 'Ольга',
  [secondaryId]: 'olga-1'
}

for (const prop in user) {
  console.log(prop, user[prop])
}
// id 193
// name Ольга

console.log(user[secondaryId])
// olga-1

        
        
          
        
      

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

Системные символы

Скопировано

Символы активно используются внутри самого JavaScript, чтобы определять поведение объектов. Такие символы называются «хорошо известными» (well-known symbols).

Самый известный символ Symbol.iterator, который позволяет реализовать обход конструкции с помощью синтаксических конструкций for..of и спред-синтаксиса.

Полный список таких символов доступен в спецификации, но на практике он нужен нечасто.

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

Скопировано

Созданный символ уникален, но как быть, если он нужен в нескольких местах программы? Для решения этой проблемы существует глобальный реестр символов, он хранит символы по строковым ключам. При обращении по ключу всегда будет возвращаться один и тот же символ.

Работа с реестром символов организована с помощью двух методов:

  • Symbol.for(ключ) — возвращает символ, хранящийся по ключу. Если символа ещё не существует, он создаётся автоматически.
  • Symbol.keyFor(символ) — возвращает строковый ключ, который хранит переданный символ или undefined, если символ не хранится в реестре.
        
          
          const secondaryId = Symbol()const user = {  'id': 193,  'name': 'Ольга',  [secondaryId]: 'olga-1'}console.log(Symbol.keyFor(secondaryId))// undefinedconst newSym = Symbol.for('registryKey')const newestSym = Symbol.for('registryKey')console.log(newSym === newestSym)// trueuser[newSym] = 'hello'console.log(Symbol.keyFor(newSym))// registryKey
          const secondaryId = Symbol()

const user = {
  'id': 193,
  'name': 'Ольга',
  [secondaryId]: 'olga-1'
}

console.log(Symbol.keyFor(secondaryId))
// undefined

const newSym = Symbol.for('registryKey')
const newestSym = Symbol.for('registryKey')
console.log(newSym === newestSym)
// true

user[newSym] = 'hello'
console.log(Symbol.keyFor(newSym))
// registryKey

        
        
          
        
      

На собеседовании

Скопировано

Это партнёрская рубрика, мы выпускаем её совместно с сервисом онлайн-образования Яндекс Практикум. Приносите вопрос, на который не знаете ответа, в задачи, мы разложим всё по полочкам и опубликуем. Если знаете ответ, присылайте пулреквест на GitHub.

kyzinatra  отвечает

Скопировано

Что такое стратегия?

Скопировано

Стратегия (Strategy) — это ООП поведенческий шаблон проектирования, который позволяет расширять базовый класс или метод новым функционалом. Для этого нужно передать в него, так называемый, конкретный класс. Например:

        
          
          const Clock = new Clock // Создаем базовую версию часовconst GoldenClock = new Clock(new GoldenParts) // Часы с золотым оформлениемconst WoodenClock = new Clock(new WoodenParts) // Часы с деревянным оформлениемClock.draw() // Рисуем стандартную версию часовGoldenClock.draw() // Метод тот же, что и строкой выше, но теперь мы используем золотое оформлениеWoodenClock.draw() // Используем деревянное оформление
          const Clock = new Clock // Создаем базовую версию часов
const GoldenClock = new Clock(new GoldenParts) // Часы с золотым оформлением
const WoodenClock = new Clock(new WoodenParts) // Часы с деревянным оформлением

Clock.draw() // Рисуем стандартную версию часов
GoldenClock.draw() // Метод тот же, что и строкой выше, но теперь мы используем золотое оформление
WoodenClock.draw() // Используем деревянное оформление

        
        
          
        
      

Выполнение задачи

Скопировано

Чтобы написать такой класс нам понадобится специальный well-known символ [Symbol.split]. Метод split() вызывает функцию [Symbol.split](str), а результат вызова возвращает как результат split(). У строк это уже реализовано:

        
          
          "123,4,56".split(",") // ["123", "4", "56]
          "123,4,56".split(",") // ["123", "4", "56]

        
        
          
        
      

Мы можем добавить такое же поведение к своему классу:

        
          
          class MySplit {  constructor(value) {    this.value = value; // Принимаем строку по которой будем сплитить  }  [Symbol.split](string) { // принимаем строку-аргумент (которую будем сплитить)    // В конце необходимо вернуть полученный результат, иначе split() вернет undefined  }}
          class MySplit {
  constructor(value) {
    this.value = value; // Принимаем строку по которой будем сплитить
  }
  [Symbol.split](string) { // принимаем строку-аргумент (которую будем сплитить)
    // В конце необходимо вернуть полученный результат, иначе split() вернет undefined
  }
}

        
        
          
        
      

Для нашей задачи разобьём строку при помощи регулярного выражения:

        
          
          [Symbol.split](string) {  // Заменяем все вхождения this.value на /${this.value}/  let index = string.replace(new RegExp(this.value, "g"), `/${this.value}/`);  // убираем первый слэш, /url/Path ->  url/Path  if (index[0] === "/") index = index.substr(1)  // Строка должна начинаться с url/, даже если его не было в начале  if (!index.startsWith(this.value)) index = `${this.value}/` + index;  return index;}
          [Symbol.split](string) {
  // Заменяем все вхождения this.value на /${this.value}/
  let index = string.replace(new RegExp(this.value, "g"), `/${this.value}/`);

  // убираем первый слэш, /url/Path ->  url/Path
  if (index[0] === "/") index = index.substr(1)
  // Строка должна начинаться с url/, даже если его не было в начале
  if (!index.startsWith(this.value)) index = `${this.value}/` + index;

  return index;
}

        
        
          
        
      

Пример работы

Скопировано
        
          
          "foobarfoobaz".split(new MySplit("foo")) // "foo/var/foo/baz""foobarfoobaz".split(new MySplit("bar")) // "bar/foo/bar/foobaz"
          "foobarfoobaz".split(new MySplit("foo")) // "foo/var/foo/baz"
"foobarfoobaz".split(new MySplit("bar")) // "bar/foo/bar/foobaz"

        
        
          
        
      

Что хотят проверить?

Скопировано

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