Symbol primitives and well-known symbols for creating unique identifiers, implementing protocols, and customizing built-in JavaScript behavior.
Core Symbol functionality for creating unique identifiers and managing global symbol registry.
/**
* Symbol constructor and static methods
*/
interface SymbolConstructor {
/**
* Creates a new unique Symbol
* @param description - Optional string description for debugging
*/
(description?: string | number): symbol;
/**
* Creates or retrieves a symbol from the global symbol registry
* @param key - String key to identify the symbol
*/
for(key: string): symbol;
/**
* Retrieves the key for a symbol in the global registry
* @param symbol - Symbol to get the key for
*/
keyFor(symbol: symbol): string | undefined;
}Usage Examples:
import Symbol from 'core-js-pure/stable/symbol';
// Creating unique symbols
const sym1 = Symbol('description');
const sym2 = Symbol('description');
console.log(sym1 === sym2); // false - each symbol is unique
// Global symbol registry
const globalSym1 = Symbol.for('shared-key');
const globalSym2 = Symbol.for('shared-key');
console.log(globalSym1 === globalSym2); // true - same symbol from registry
console.log(Symbol.keyFor(globalSym1)); // 'shared-key'
console.log(Symbol.keyFor(sym1)); // undefined - not in global registry
// Using symbols as object keys
const obj = {};
const privateKey = Symbol('private');
obj[privateKey] = 'secret value';
obj.public = 'public value';
console.log(Object.keys(obj)); // ['public'] - symbol keys not enumerated
console.log(obj[privateKey]); // 'secret value'Standard symbols that customize built-in JavaScript behavior and implement various protocols.
interface SymbolConstructor {
/**
* Symbol for async disposal in using declarations
*/
readonly asyncDispose: unique symbol;
/**
* Symbol for async iteration protocol
*/
readonly asyncIterator: unique symbol;
/**
* Symbol for disposal in using declarations
*/
readonly dispose: unique symbol;
/**
* Symbol to customize instanceof behavior
*/
readonly hasInstance: unique symbol;
/**
* Symbol to control Array.prototype.concat spreading
*/
readonly isConcatSpreadable: unique symbol;
/**
* Symbol for iteration protocol
*/
readonly iterator: unique symbol;
/**
* Symbol to customize String.prototype.match behavior
*/
readonly match: unique symbol;
/**
* Symbol to customize String.prototype.matchAll behavior
*/
readonly matchAll: unique symbol;
/**
* Symbol to customize String.prototype.replace behavior
*/
readonly replace: unique symbol;
/**
* Symbol to customize String.prototype.search behavior
*/
readonly search: unique symbol;
/**
* Symbol to specify constructor function for derived objects
*/
readonly species: unique symbol;
/**
* Symbol to customize String.prototype.split behavior
*/
readonly split: unique symbol;
/**
* Symbol to customize primitive conversion behavior
*/
readonly toPrimitive: unique symbol;
/**
* Symbol to customize Object.prototype.toString behavior
*/
readonly toStringTag: unique symbol;
/**
* Symbol to specify properties excluded from with statement
*/
readonly unscopables: unique symbol;
}Usage Examples:
import Symbol from 'core-js-pure/stable/symbol';
// Custom iterator implementation
class CountDown {
constructor(start) {
this.start = start;
}
[Symbol.iterator]() {
let current = this.start;
return {
next() {
if (current > 0) {
return { value: current--, done: false };
} else {
return { done: true };
}
}
};
}
}
const countdown = new CountDown(3);
console.log([...countdown]); // [3, 2, 1]
// Custom string tag for better debugging
class MyClass {
[Symbol.toStringTag] = 'MyClass';
}
const instance = new MyClass();
console.log(Object.prototype.toString.call(instance)); // '[object MyClass]'
// Custom primitive conversion
class Temperature {
constructor(celsius) {
this.celsius = celsius;
}
[Symbol.toPrimitive](hint) {
switch (hint) {
case 'number':
return this.celsius;
case 'string':
return `${this.celsius}°C`;
default:
return this.celsius;
}
}
}
const temp = new Temperature(25);
console.log(+temp); // 25 (number conversion)
console.log(`${temp}`); // '25°C' (string conversion)
console.log(temp + 10); // 35 (default conversion to number)Using symbols to implement custom protocols and behaviors.
/**
* Examples of implementing custom protocols with symbols
*/
// Custom matcher protocol
interface Matcher {
[Symbol.match](string: string): RegExpMatchArray | null;
[Symbol.replace](string: string, replaceValue: string): string;
[Symbol.search](string: string): number;
[Symbol.split](string: string, limit?: number): string[];
}
// Custom species constructor
interface CustomArray<T> extends Array<T> {
static [Symbol.species]: ArrayConstructor;
}
// Custom iteration protocol
interface CustomIterable<T> {
[Symbol.iterator](): Iterator<T>;
}
// Custom async iteration protocol
interface CustomAsyncIterable<T> {
[Symbol.asyncIterator](): AsyncIterator<T>;
}Usage Examples:
import Symbol from 'core-js-pure/stable/symbol';
// Custom matcher that works with string methods
class WordMatcher {
constructor(word) {
this.word = word;
}
[Symbol.match](str) {
const index = str.indexOf(this.word);
return index !== -1 ? [this.word] : null;
}
[Symbol.replace](str, replacement) {
return str.replace(this.word, replacement);
}
[Symbol.search](str) {
return str.indexOf(this.word);
}
[Symbol.split](str) {
return str.split(this.word);
}
}
const matcher = new WordMatcher('hello');
const text = 'Say hello to the world';
console.log(text.match(matcher)); // ['hello']
console.log(text.replace(matcher, 'hi')); // 'Say hi to the world'
console.log(text.search(matcher)); // 4
console.log(text.split(matcher)); // ['Say ', ' to the world']
// Custom async iterable
class AsyncRange {
constructor(start, end, delay = 100) {
this.start = start;
this.end = end;
this.delay = delay;
}
[Symbol.asyncIterator]() {
let current = this.start;
const end = this.end;
const delay = this.delay;
return {
async next() {
if (current <= end) {
await new Promise(resolve => setTimeout(resolve, delay));
return { value: current++, done: false };
}
return { done: true };
}
};
}
}
// Usage with for-await-of
const asyncRange = new AsyncRange(1, 3, 500);
for await (const num of asyncRange) {
console.log(num); // Prints 1, 2, 3 with 500ms delay between each
}Using Symbol.hasInstance to customize instanceof behavior.
/**
* Custom instanceof behavior using Symbol.hasInstance
*/
interface Constructor {
[Symbol.hasInstance](instance: any): boolean;
}Usage Examples:
import Symbol from 'core-js-pure/stable/symbol';
// Custom instanceof behavior
class Collection {
static [Symbol.hasInstance](instance) {
return Array.isArray(instance) ||
instance instanceof Set ||
instance instanceof Map;
}
}
console.log([] instanceof Collection); // true
console.log(new Set() instanceof Collection); // true
console.log(new Map() instanceof Collection); // true
console.log({} instanceof Collection); // false
// Type checking utility
class TypeChecker {
static [Symbol.hasInstance](instance) {
return typeof instance === 'string' ||
typeof instance === 'number' ||
typeof instance === 'boolean';
}
}
console.log('hello' instanceof TypeChecker); // true
console.log(42 instanceof TypeChecker); // true
console.log(true instanceof TypeChecker); // true
console.log({} instanceof TypeChecker); // falseControlling how objects behave when used with Array.prototype.concat.
/**
* Control concat spreading behavior
*/
interface ConcatSpreadable {
[Symbol.isConcatSpreadable]: boolean;
}Usage Examples:
import Symbol from 'core-js-pure/stable/symbol';
// Array-like object that spreads in concat
const arrayLike = {
0: 'a',
1: 'b',
2: 'c',
length: 3,
[Symbol.isConcatSpreadable]: true
};
console.log([1, 2].concat(arrayLike)); // [1, 2, 'a', 'b', 'c']
// Array that doesn't spread
const nonSpreadingArray = ['x', 'y'];
nonSpreadingArray[Symbol.isConcatSpreadable] = false;
console.log([1, 2].concat(nonSpreadingArray)); // [1, 2, ['x', 'y']]
// Custom collection class
class CustomCollection extends Array {
constructor(...items) {
super(...items);
// Control whether instances spread in concat
this[Symbol.isConcatSpreadable] = false;
}
}
const collection = new CustomCollection('a', 'b');
console.log([1, 2].concat(collection)); // [1, 2, CustomCollection(['a', 'b'])]Using symbols to create private-like properties that won't conflict with string keys.
import Symbol from 'core-js-pure/stable/symbol';
// Private properties using symbols
const _private = Symbol('private');
const _secret = Symbol('secret');
class SecureClass {
constructor(publicData, privateData) {
this.public = publicData;
this[_private] = privateData;
this[_secret] = 'top secret';
}
getPrivate() {
return this[_private];
}
static getSecret(instance) {
return instance[_secret];
}
}
const obj = new SecureClass('public info', 'private info');
console.log(obj.public); // 'public info'
console.log(obj.getPrivate()); // 'private info'
console.log(Object.keys(obj)); // ['public'] - symbols not enumeratedCreating a framework for implementing custom protocols.
import Symbol from 'core-js-pure/stable/symbol';
// Protocol registry
const protocols = new Map();
const defineProtocol = (name, symbol) => {
protocols.set(name, symbol);
return symbol;
};
const implementsProtocol = (obj, protocolName) => {
const symbol = protocols.get(protocolName);
return symbol && typeof obj[symbol] === 'function';
};
// Define custom protocols
const Serializable = defineProtocol('Serializable', Symbol('Serializable'));
const Comparable = defineProtocol('Comparable', Symbol('Comparable'));
const Cloneable = defineProtocol('Cloneable', Symbol('Cloneable'));
// Implement protocols
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
[Serializable]() {
return JSON.stringify({ name: this.name, age: this.age });
}
[Comparable](other) {
return this.age - other.age;
}
[Cloneable]() {
return new Person(this.name, this.age);
}
}
const person = new Person('Alice', 30);
console.log(implementsProtocol(person, 'Serializable')); // true
console.log(person[Serializable]()); // '{"name":"Alice","age":30}'Using global symbols for cross-realm (iframe, worker) communication.
import Symbol from 'core-js-pure/stable/symbol';
// Global symbols work across different JavaScript realms
const SHARED_PROTOCOL = Symbol.for('app.shared.protocol');
const MESSAGE_TYPE = Symbol.for('app.message.type');
// In main window
const createMessage = (type, data) => ({
[MESSAGE_TYPE]: type,
data,
timestamp: Date.now()
});
// In worker or iframe (different realm)
const handleMessage = (message) => {
const type = message[MESSAGE_TYPE];
switch (type) {
case 'user-action':
return processUserAction(message.data);
case 'system-event':
return processSystemEvent(message.data);
}
};
// Protocol works across realms because symbols are global
const userMessage = createMessage('user-action', { action: 'click' });
handleMessage(userMessage); // Works even in different realm