Друкарня від WE.UA

Шпаргалка по JavaScript Symbol-ам

Що таке Symbol в JavaScript

Symbol — один з примітивних типів даних. Це дещо своєрідне та може бути складнішим в розумінні.

Виклик `Symbol()` створює новий символ, значення якого гарантовано відрізняється від будь-яких створений раніше символів.

const sym1 = Symbol();
const sym2 = Symbol();
sym1 === sym2; // false

Що ж це за значення? Це не текст, не літера, не число. У символів немає якогось текстового представлення. Тому якщо ви спробуєте вивести символ в консоль, ви побачите лише “Symbol()” — факт того, що це символ, але не його “значення”, бо його заховано глибоко в реалізації рушія.

console.log( Symbol() ); // Symbol() 🤷‍♂️

Саме гарантія неповторюваності є ключовою механікою символів. Новостворена змінна єдиний спосіб використовувати той самий символ.

function createObj() {
  const sym = Symbol()
  const obj = {
    [sym]: 'Привіт, Світе!'
  };

  console.log( obj[sym] ) // Привіт, Світе!

  return obj;
}

const obj2 = createObj();
console.log(obj) // { Symbol(): "Привіт, Світе!" }

Вивівши структуру obj2 ви можете побачити, що в ньому є властивість за ключем-символом, але ви не можете отримати доступ до цієї властивості, оскільки змінна-ключ залишилась в області видимості createObj().

Для чого вони потрібні

Оскільки символ це примітив, то його можна використовувати як ключ для властивостей об’єктів.

В моїй практиці при розробці бібліотеки я кілька разів опирався на символи, щоб домішувати щось в користувацькі об’єкти та гарантувати, що я не перезапишу якісь дані.

const cachedKey = Symbol()

export function doSomeJobWithObjectAndCacheResult(obj) {
  if (obj[cachedKey] === undefined) {
    obj[cachedKey] = doSomeJobWithObject(obj)
  }

  return obj[cachedKey]
}
  1. Оскільки кожен створений символ це унікальний примітив, то функція вище гарантує, що в переданому об’єкті не може бути поля за цим символом створеним десь окрім як цією функцією.

  2. Доступ до цього поля неможливий за межами цієї функції.

Які є вбудовані Symbol

В JavaScript чимало символів створюються та використовуються замовчуванням. Всі вони визначені у статичних властивостях Symbol.

console.dir(Symbol) /*
{
  name: "Symbol"
  asyncIterator: Symbol("Symbol.asyncIterator"),
  hasInstance: Symbol("Symbol.hasInstance"),
  isConcatSpreadable: Symbol("Symbol.isConcatSpreadable"),
  iterator: Symbol("Symbol.iterator"),
  match: Symbol("Symbol.match"),
  matchAll: Symbol("Symbol.matchAll"),
  replace: Symbol("Symbol.replace"),
  search: Symbol("Symbol.search"),
  species: Symbol("Symbol.species"),
  split: Symbol("Symbol.split"),
  toPrimitive: Symbol("Symbol.toPrimitive"),
  toStringTag: Symbol("Symbol.toStringTag"),
  unscopables: Symbol("Symbol.unscopables"),
}
*/

І ви їх використовуєте, навіть не усвідомлюючи цього. Наприклад, викликаючи цикл for на масиві:

const arr = [1,2,3]

for (const num of arr) {
  console.log(num)
}

// 1
// 2
// 3

У масивів є прихований метод, який вираховує та повертає значення для кожної ітерації (такі функції називаються ітераторами). А захований цей метод за символом Symbol.iterator.

Тобто, під час роботи циклу рушій JavaScript викликає прихований метод `arr[Symbol.iterator]()` та записує його результат в змінну num. І повторює цей процес доти, доки `arr[Symbol.iterator]()` повертає бодай щось.

І ми можемо змінювати поведінку JavaScript змінюючи приховані методи за такими символами:

const arr = [1,2,3]

arr[Symbol.iterator] = function* () {
  yield 'Що це?';
  yield 'Це взагалі законно?';
  yield 'Чортівня! 🪄';
} 

for (const num of arr) {
  console.log(num)
}

// Що це?
// Це взагалі законно?
// Чортівня! 🪄

Змінивши метод за Symbol.iterator ми фактично переписали реалізацію перебору значень масиву. І це працює не лише для циклів

const newArr = [...arr]
console.log(newArr)
// [ "Що це?", "Це взагалі законно?", "Чортівня! 🪄" ]

Шпаргалка по JavaScript Symbol-ам

Symbol.hasInstance

Метод, який визначає, чи розпізнає об’єкт-конструктор інший об’єкт як свій екземпляр. Використовується в `instanceof`.

obj instanceof Array // obj[Symbol.hasInstance](Array)

Symbol.isConcatSpreadable

Логічне значення, яке визначає, чи потрібно “розгортати” значення при конкатенації з масивом, чи залишати як є. Використовується в `Array.prototype.concat()`.

const arr1 = [1,2,3]
const arr2 = [4,5,6]

console.log(arr1.concat(arr2)) // [1,2,3,4,5,6]

arr2[Symbol.isConcatSpreadable] = false

console.log(arr1.concat(arr2)) // [1,2,3,[4,5,6]]

Symbol.iterator та Symbol.asyncIterator

Про цього написав трохи вище. Методи, які повертають ітератори об’єкта.

Symbol.match та Symbol.matchAll

Методи які використовуються в `String.prototype.match()` та `String.prototype.matchAll()` відповідно.

'foo'.match(obj)    // obj[Symbol.match]('foo')
'foo'.matchAll(obj) // obj[Symbol.matchAll]('foo')

Symbol.replace

Метод, який визначає як буде замінено частину рядка. Використовується в `String.prototype.replace()`.

'foo'.replace(obj, 'bar') // obj[Symbol.replace]('foo', 'bar')

Symbol.search

Метод який повертає індекс символу який відповідає об’єкту. Використовується в `String.prototype.search()`.

'foo'.search(obj) // obj[Symbol.search]('foo')

Symbol.species

Функція конструктор. Методи, що створюють копії об’єктів, можуть звертатись до цього символу, щоб змінювати прототип для новоствореної копії.

class MyArray extends Array {
  // Визначає, що всі копії будуть вважати Array за батьківський конструктор
  static get [Symbol.species]() {
    return Array;
  }
}
const a = new MyArray(1, 2, 3);
const mapped = a.map((x) => x * x);

console.log(a.constructor); // MyArray 
console.log(mapped.constructor); // Array

Symbol.split

Метод, який розрізає рядок за переданим регулярним виразом. Використовується в `String.prototype.split()`.

'foo'.split(obj, limit) // obj[Symbol.split]('foo', limit)

Symbol.toPrimitive

Метод, який конвертує об’єкт в примітив. Використовується при приведенні типів.

const obj = {};
console.log(+obj);     // NaN
console.log(`${obj}`); // "[object Object]"
console.log(obj + ""); // "[object Object]"

// Змінюємо алгоритм конвертації об'єкту в примітивні значення.
obj[Symbol.toPrimitive] = function(hint) {
  if (hint === "number") {
    return 42;
  }
  if (hint === "string") {
    return "Привіт";
  }
  return true;
};

console.log(+obj);     // 42       — hint === "number"
console.log(`${obj}`); // "Привіт" — hint === "string"
console.log(obj + ""); // "true"   — hint === "default"

Symbol.unscopables

Об’єкт, ключі якого буде виключено з області видимості with

const obj {
  name: 'Alex'
  password: '1234'

  [Symbol.unscopables]: {
    password: true
  }
}

with(obj) {
  console.log(name) // Alex
  console.log(password) // undefined
}

Підсумок

Як бачите, чимала частина “внутрішньої” поведінки JavaScript визначена в прихованих за символами методах. І хоч я й називаю їх прихованими, насправді ви вільні маніпулювати ними як завгодно, створювати власні унікальні класи з нестандартною поведінкою.

Статті про вітчизняний бізнес та цікавих людей:

  • Як побудована програма Meest China Academy

    Курс про товарний бізнес охоплює різні етапи роботи: від пошуку ідеї до перевірки товару, логістики та масштабування. Програма Meest China Academy містить 17 модулів, які послідовно розкривають ці теми без зведення всього навчання до однієї універсальної поради.

    Теми цього довгочиту:

    Meest
  • Які технології використані в Айфон 17

    Айфон 17 поєднує OLED-дисплей із частотою до 120 Гц, чип A19, дві камери Fusion 48 Мп і швидке заряджання через USB-C. У COMFY можна купити Айфон 17 з накопичувачем на 256 або 512 ГБ, вибравши конфігурацію відповідно до обсягу фото, відео та застосунків

    Теми цього довгочиту:

    Iphone 17
  • Як вибрати вживаний iPhone: коротке керівництво для розумної покупки

    Вторинний ринок смартфонів Apple активно зростає, і питання, як вибрати вживаний iPhone, стає актуальним для дедалі більшої кількості користувачів. Правильний підхід дозволяє отримати бу iPhone з актуальною iOS і доброю камерою за помітно нижчу ціну, ніж у нових пристроїв.

    Теми цього довгочиту:

    Iphone
  • Як подолати жіночу безплідність за допомогою донорських яйцеклітин

    Діагноз "безпліддя" рідко звучить як вирок одразу. Для багатьох жінок саме в цей момент вперше звучить словосполучення "донорські яйцеклітини" — і це часто не кінець шляху до материнства, а якраз новий, реальний шанс.

    Теми цього довгочиту:

    Донорство
  • Як заспокоїти зубний біль і чому самолікування може призвести до операції?

    Раптовий біль у зубі може призвести до порушення сну та ускладненого вживання їжі. Люди часто приймають таблетки, роблять полоскання або лікуються народними засобами. В таких випадках не варто відкладати візит до стоматолога, адже можуть з’явитись ускладнення.

    Теми цього довгочиту:

    Стоматологія
Поділись своїми ідеями в новій публікації.
Ми чекаємо саме на твій довгочит!
Олександр Козак
Олександр Козак@kozack

16Довгочити
10.3KПерегляди
69Підписники
Підтримати
На Друкарні з 14 квітня 2023

Більше від автора

  • Детальний огляд семантики HTML теґів. Різниця між <section> та <article>

    Огляд семантики HTML: розрізнення між <section> та <article>, значення правильного використання тегів та їх роль у структурі вебсторінки. Дослідження важливості семантики для SEO та доступності, а також рекомендації щодо використання HTML-елементів та ролей

    Теми цього довгочиту:

    Html
  • Про доступність та UI на прикладі мого особистого сайту

    Знайомтесь це мій особистий сайт. Перший знімок те яким він був ДО другий - ПІСЛЯ сьогоднішнього патчу. І в цій короткій статті я хочу зробити маленьке ревью та розказати на прикладах ЧОМУ я реалізував деякі дивні речі.

    Теми цього довгочиту:

    Доступність
  • Як зробити нескінченну прокрутку на сайті. Або 10 недоліків та одна перевага Infinity Scroll

    В цій статті я хочу описати 10 поширених проблем, пов’язаних з нескінченною прокруткою, які розробнику доведеться вирішувати при реалізації Infinity Scroll. Та про єдину причину, чому попри всі недоліки нескінченну прокрутку активно використовують.

    Теми цього довгочиту:

    Ui-ux

Це також може зацікавити:

Коментарі (3)

Класний матеріал, дякую! Особливо повеселилась на моменті, де можна переписати цикл for. Збираюсь тепер трохи потролити своїх джунів)))

Це також може зацікавити: