83. Symbol
Symbol — уникальный примитивный тип данных в JavaScript. Каждый Symbol гарантированно уникален, даже если создан с одинаковым описанием. Используется для создания скрытых свойств объектов и реализации специального поведения через well-known symbols.
Создание символов
Заголовок раздела «Создание символов»const sym1 = Symbol()const sym2 = Symbol('описание')const sym3 = Symbol('описание')
console.log(sym1 === sym2) // false — каждый Symbol уникаленconsole.log(sym2 === sym3) // false — даже с одинаковым описанием!console.log(typeof sym1) // 'symbol'console.log(sym2.toString()) // 'Symbol(описание)'console.log(sym2.description) // 'описание'Символы как ключи объектов
Заголовок раздела «Символы как ключи объектов»Главное применение Symbol — создание «скрытых» свойств, которые не попадают в обычные итерации:
const id = Symbol('id')const name = Symbol('name')
const user = { [id]: 123, [name]: 'Алексей', login: 'alexei',}
// Обычная итерация не показывает Symbol-свойстваconsole.log(Object.keys(user)) // ['login']console.log(JSON.stringify(user)) // '{"login":"alexei"}'
// Получить Symbol-свойства можно явноconsole.log(user[id]) // 123console.log(Object.getOwnPropertySymbols(user)) // [Symbol(id), Symbol(name)]Symbol.for — глобальный реестр
Заголовок раздела «Symbol.for — глобальный реестр»Symbol.for(key) создаёт символ в глобальном реестре. Одинаковый ключ — один и тот же символ, даже из разных модулей:
const s1 = Symbol.for('shared')const s2 = Symbol.for('shared')
console.log(s1 === s2) // true — один символ из реестра!console.log(Symbol.keyFor(s1)) // 'shared'
// Обычный Symbol не попадает в реестрconst local = Symbol('local')console.log(Symbol.keyFor(local)) // undefinedWell-known символы
Заголовок раздела «Well-known символы»JavaScript использует встроенные символы для настройки поведения объектов.
Symbol.iterator
Заголовок раздела «Symbol.iterator»Позволяет сделать объект итерируемым:
const range = { from: 1, to: 5, [Symbol.iterator]() { let current = this.from const last = this.to return { next() { return current <= last ? { value: current++, done: false } : { value: undefined, done: true } } } }}
for (const num of range) { console.log(num) // 1, 2, 3, 4, 5}
console.log([...range]) // [1, 2, 3, 4, 5]Symbol.toPrimitive
Заголовок раздела «Symbol.toPrimitive»Управляет приведением объекта к примитиву:
const money = { amount: 100, currency: 'USD', [Symbol.toPrimitive](hint) { if (hint === 'number') return this.amount if (hint === 'string') return `${this.amount} ${this.currency}` return this.amount // 'default' }}
console.log(+money) // 100 (number hint)console.log(`${money}`) // '100 USD' (string hint)console.log(money + 50) // 150 (default hint)Symbol.hasInstance
Заголовок раздела «Symbol.hasInstance»Настраивает оператор instanceof:
class EvenNumber { static [Symbol.hasInstance](num) { return Number.isInteger(num) && num % 2 === 0 }}
console.log(2 instanceof EvenNumber) // trueconsole.log(3 instanceof EvenNumber) // falseconsole.log(4 instanceof EvenNumber) // trueДругие well-known символы
Заголовок раздела «Другие well-known символы»| Символ | Назначение |
|---|---|
Symbol.iterator | Итерируемость (for...of, spread) |
Symbol.asyncIterator | Асинхронная итерация |
Symbol.toPrimitive | Приведение к примитиву |
Symbol.toStringTag | Результат Object.prototype.toString |
Symbol.hasInstance | Поведение instanceof |
Symbol.species | Конструктор для создания производных объектов |
Практический пример: приватные поля через Symbol
Заголовок раздела «Практический пример: приватные поля через Symbol»// Паттерн "скрытые свойства" через Symbolconst _balance = Symbol('balance')const _owner = Symbol('owner')
class BankAccount { constructor(owner, initialBalance) { this[_owner] = owner this[_balance] = initialBalance }
deposit(amount) { if (amount <= 0) throw new Error('Сумма должна быть положительной') this[_balance] += amount return this }
withdraw(amount) { if (amount > this[_balance]) throw new Error('Недостаточно средств') this[_balance] -= amount return this }
toString() { return `Счёт ${this[_owner]}: ${this[_balance]} руб.` }}
const account = new BankAccount('Алексей', 1000)account.deposit(500).withdraw(200)console.log(account.toString()) // 'Счёт Алексей: 1300 руб.'
// Свойство "скрыто" от обычного доступаconsole.log(Object.keys(account)) // []console.log(account.balance) // undefinedЗадания для практики
Заголовок раздела «Задания для практики»- Создайте Symbol
SECRET_KEYи используйте его как ключ в объекте конфигурации. - Реализуйте итерируемый класс
Fibonacci, генерирующий числа Фибоначчи черезSymbol.iterator. - Добавьте
Symbol.toPrimitiveк классуTemperatureдля конвертации в Celsius/Fahrenheit. - Используйте
Symbol.for('validator')для создания общего символа валидации между модулями.