Symbol и приватность
Symbol-ключи не перечисляются стандартными методами (
for...in,Object.keys(),JSON.stringify), что позволяет использовать их как псевдо-приватные свойства объектов до появления нативных приватных полей класса (#field).
Зачем нужно
До ES2022 с приватными полями класса Symbol был основным способом скрыть «внутренние» свойства объекта от случайного перечисления и конфликтов имён. Это полезно в библиотеках: metadata, internal state, уникальные маркеры не загрязняют публичный API объекта.
Где используется
- Хранение внутреннего состояния в объектах без загрязнения namespace
- Уникальные маркеры типов (type tags) в библиотеках
- Метаданные объектов, не видимые пользователю
- Расширение сторонних объектов без риска конфликтов
Основной контент
Symbol как скрытые ключи
// Symbol-ключи невидимы для стандартных перечислений
const _private = Symbol('_private');
const _version = Symbol('_version');
const obj = {
name: 'PublicAPI',
[_private]: 'secret data',
[_version]: '2.1.0',
getValue { return this[_private]; }
};
// Обычное перечисление не видит Symbol
console.log(Object.keys(obj)); // ['name', 'getValue'] — без Symbol!
console.log(JSON.stringify(obj)); // '{"name":"PublicAPI"}' — без Symbol!
for (const key in obj) {
console.log(key); // только 'name', 'getValue'
}
// Но доступны напрямую если есть ссылка на Symbol
console.log(obj[_private]); // 'secret data'
// И через специальный метод
console.log(Object.getOwnPropertySymbols(obj)); // [Symbol(_private), Symbol(_version)]
Сравнение с WeakMap для приватности
// Вариант 1: Symbol (псевдо-приватный)
const _count = Symbol('count');
class Counter {
constructor { this[_count] = 0; }
increment { this[_count]++; }
get { return this[_count]; }
}
// _count виден через Object.getOwnPropertySymbols
// Вариант 2: WeakMap (настоящая приватность)
const _state = new WeakMap();
class SecureCounter {
constructor { _state.set(this, { count: 0 }); }
increment { _state.get(this).count++; }
get { return _state.get(this).count; }
}
// Состояние вообще недоступно извне
// Вариант 3: Private fields ES2022 (самый современный)
class ModernCounter {
#count = 0;
increment { this.#count++; }
get { return this.#count; }
}
Symbol как уникальные type tags
// Уникальный маркер типа — не конфликтует с другими библиотеками
const OBSERVABLE = Symbol('Observable');
const IMMUTABLE = Symbol('Immutable');
function makeObservable(obj) {
return { ...obj, [OBSERVABLE]: true };
}
function isObservable(obj) {
return obj[OBSERVABLE] === true;
}
const state = makeObservable({ count: 0 });
console.log(isObservable(state)); // true
console.log(isObservable({ count: 0 })); // false
// Не конфликтует: другая библиотека создаст свой Symbol('Observable')
const LIB_OBSERVABLE = Symbol('Observable'); // другой Symbol!
console.log(OBSERVABLE === LIB_OBSERVABLE); // false
Расширение сторонних объектов
// Безопасное добавление свойств к объектам сторонних API
const METADATA = Symbol('metadata');
function attachMetadata(apiResponse, meta) {
apiResponse[METADATA] = meta;
return apiResponse;
}
const user = attachMetadata(
{ id: 1, name: 'Иван' }, // сторонний объект
{ fetchedAt: Date.now(), source: 'api' }
);
// Публичный API объекта не изменён
console.log(Object.keys(user)); // ['id', 'name']
console.log(user[METADATA]); // { fetchedAt: ..., source: 'api' }
Частые ошибки
- Symbol — не настоящая приватность —
Object.getOwnPropertySymbolsраскрывает все Symbol-ключи. Для истинной приватности используйте приватные поля (#field) или WeakMap. - Потеря Symbol при копировании —
Object.assign({}, obj)и{...obj}НЕ копируют Symbol-ключи. ИспользуйтеObject.assignс явным указанием илиObject.getOwnPropertySymbolsдля ручного копирования. - Сериализация — Symbol-свойства невидимы для
JSON.stringify. Если нужна сериализация этих данных, Symbol не подходит.
Связанные темы
Ресурсы
🎓 Источник: Архив 2018 - Часть 5 EventEmitter, Symbol, Proxy
- 📅 2020-01-05 · YouTube
- Тезисы: Symbol даёт частичную приватность — не виден в
for...in,Object.keys(),JSON.stringify. Но доступен черезObject.getOwnPropertySymbolsилиReflect.ownKeys. Для полной приватности — WeakMap или#field
🎓 Источник: Explicit resource management with Using и Symbol.dispose
- 📅 2025-05-22 · YouTube
- Тезисы: Well-known Symbols используются для языковых протоколов.
Symbol.dispose— proposal для автоматической очистки ресурсов (аналог IDisposable)