Docs
README
13.3 Symbols
Master JavaScript's seventh primitive type - unique, immutable identifiers that enable powerful metaprogramming capabilities.
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā ā
ā āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā ā
ā ā SYMBOLS ā ā
ā ā ā ā
ā ā "The secret identity of JavaScript objects" ā ā
ā āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā ā
ā ā
ā āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā ā
ā ā ā ā
ā ā const sym1 = Symbol('id'); ā ā
ā ā const sym2 = Symbol('id'); ā ā
ā ā ā ā
ā ā sym1 === sym2 // false ā Always unique, even with same name! ā ā
ā ā ā ā
ā āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā ā
ā ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
š Overview
Symbols are a primitive data type introduced in ES6. Each Symbol is unique and immutable, making them perfect for creating unique property keys that won't collide with other keys - even if they have the same description.
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā THE SEVEN PRIMITIVE TYPES ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā¤
ā ā
ā 1. undefined - Uninitialized variable ā
ā 2. null - Intentional absence of value ā
ā 3. boolean - true / false ā
ā 4. number - Integers and floats ā
ā 5. string - Text ā
ā 6. bigint - Large integers (ES2020) ā
ā 7. symbol - Unique identifiers (ES2015) ā NEW! ā
ā ā
ā + Objects (non-primitive, includes arrays, functions, etc.) ā
ā ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
š§ Creating Symbols
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā CREATING SYMBOLS ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā¤
ā ā
ā // Basic Symbol creation ā
ā const sym1 = Symbol(); ā
ā const sym2 = Symbol(); ā
ā console.log(sym1 === sym2); // false - ALWAYS UNIQUE! ā
ā ā
ā // With description (for debugging only) ā
ā const sym3 = Symbol('my description'); ā
ā console.log(sym3.description); // 'my description' ā
ā console.log(sym3.toString()); // 'Symbol(my description)' ā
ā ā
ā // Same description, STILL UNIQUE ā
ā const symA = Symbol('id'); ā
ā const symB = Symbol('id'); ā
ā console.log(symA === symB); // false! ā
ā ā
ā // ā ļø Cannot use 'new Symbol()' - it's a primitive, not object! ā
ā new Symbol(); // TypeError! ā
ā ā
ā // typeof returns 'symbol' ā
ā console.log(typeof sym1); // 'symbol' ā
ā ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
š Symbol as Object Keys
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā SYMBOLS AS PROPERTY KEYS ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā¤
ā ā
ā const id = Symbol('id'); ā
ā const secret = Symbol('secret'); ā
ā ā
ā const user = { ā
ā name: 'Alice', ā
ā age: 30, ā
ā [id]: 12345, // Symbol as key (computed property) ā
ā [secret]: 'password123' ā
ā }; ā
ā ā
ā // Accessing symbol properties ā
ā console.log(user[id]); // 12345 ā
ā console.log(user.id); // undefined - different key! ā
ā console.log(user['id']); // undefined - string 'id', not Symbol ā
ā ā
ā āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā ā
ā ā SYMBOL PROPERTIES ARE HIDDEN ā ā
ā āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā ā
ā ā
ā // Regular enumeration IGNORES symbol keys: ā
ā console.log(Object.keys(user)); // ['name', 'age'] ā
ā console.log(Object.values(user)); // ['Alice', 30] ā
ā console.log(JSON.stringify(user)); // {"name":"Alice","age":30} ā
ā for (const key in user) console.log(key) // 'name', 'age' ā
ā ā
ā // To access symbol keys: ā
ā console.log(Object.getOwnPropertySymbols(user)); // [Symbol(id), ...] ā
ā console.log(Reflect.ownKeys(user)); // ['name', 'age', ...] ā
ā ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
š Global Symbol Registry
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā GLOBAL SYMBOL REGISTRY ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā¤
ā ā
ā // Symbol.for() - creates or retrieves from global registry ā
ā āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā ā
ā ā ā ā
ā ā // First call: creates and registers the symbol ā ā
ā ā const globalSym1 = Symbol.for('app.id'); ā ā
ā ā ā ā
ā ā // Second call: retrieves the SAME symbol ā ā
ā ā const globalSym2 = Symbol.for('app.id'); ā ā
ā ā ā ā
ā ā console.log(globalSym1 === globalSym2); // true! Same symbol! ā ā
ā ā ā ā
ā āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā ā
ā ā
ā // Symbol.keyFor() - retrieves the key from registry ā
ā console.log(Symbol.keyFor(globalSym1)); // 'app.id' ā
ā ā
ā // Regular symbols are NOT in the registry ā
ā const localSym = Symbol('local'); ā
ā console.log(Symbol.keyFor(localSym)); // undefined ā
ā ā
ā āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā ā
ā ā ā ā
ā ā Symbol() vs Symbol.for() ā ā
ā ā āāāāāāāāāāāāāāāāāāāāāāāāā ā ā
ā ā ā ā
ā ā Symbol('key') Local, always unique ā ā
ā ā Symbol.for('key') Global registry, shared across realms ā ā
ā ā ā ā
ā ā Use Symbol.for() when you need the same symbol across: ā ā
ā ā - Different files/modules ā ā
ā ā - Different iframes ā ā
ā ā - Service workers and main thread ā ā
ā ā ā ā
ā āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā ā
ā ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā Well-Known Symbols
JavaScript has built-in symbols that let you customize how objects behave:
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā WELL-KNOWN SYMBOLS ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā¤
ā ā
ā Symbol ā Purpose ā
ā āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā ā
ā Symbol.iterator ā Make object iterable (for...of) ā
ā Symbol.asyncIterator ā Async iteration (for await...of) ā
ā Symbol.toStringTag ā Customize Object.prototype.toString ā
ā Symbol.hasInstance ā Customize instanceof behavior ā
ā Symbol.toPrimitive ā Customize type coercion ā
ā Symbol.species ā Constructor for derived objects ā
ā Symbol.isConcatSpreadable ā Control array spread in concat ā
ā Symbol.match ā Make object work with string.match() ā
ā Symbol.replace ā Make object work with string.replace() ā
ā Symbol.search ā Make object work with string.search() ā
ā Symbol.split ā Make object work with string.split() ā
ā ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
Symbol.iterator - Make Objects Iterable
const range = {
start: 1,
end: 5,
[Symbol.iterator]() {
let current = this.start;
const end = this.end;
return {
next() {
if (current <= end) {
return { value: current++, done: false };
}
return { done: true };
},
};
},
};
// Now works with for...of!
for (const num of range) {
console.log(num); // 1, 2, 3, 4, 5
}
// And spread!
console.log([...range]); // [1, 2, 3, 4, 5]
Symbol.toStringTag - Custom Type Names
class MyClass {
get [Symbol.toStringTag]() {
return 'MyClass';
}
}
const obj = new MyClass();
console.log(Object.prototype.toString.call(obj)); // '[object MyClass]'
// Instead of '[object Object]'
Symbol.toPrimitive - Custom Type Conversion
const money = {
amount: 100,
currency: 'USD',
[Symbol.toPrimitive](hint) {
switch (hint) {
case 'number':
return this.amount;
case 'string':
return `${this.currency} ${this.amount}`;
default:
return this.amount; // 'default' hint
}
},
};
console.log(+money); // 100 (number hint)
console.log(`${money}`); // 'USD 100' (string hint)
console.log(money + 50); // 150 (default hint ā number)
š” Common Use Cases
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā SYMBOL USE CASES ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā¤
ā ā
ā 1. UNIQUE PROPERTY KEYS - Avoid naming collisions ā
ā āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā ā
ā ā // Third-party library adds data to your objects ā ā
ā ā const libraryData = Symbol('library-internal'); ā ā
ā ā user[libraryData] = { timestamp: Date.now() }; ā ā
ā ā // Won't conflict with user.libraryData string key ā ā
ā āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā ā
ā ā
ā 2. PRIVATE-ISH PROPERTIES - Hidden from normal enumeration ā
ā āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā ā
ā ā const _internal = Symbol('internal'); ā ā
ā ā class Widget { ā ā
ā ā constructor() { ā ā
ā ā this[_internal] = { state: 'active' }; ā ā
ā ā } ā ā
ā ā } ā ā
ā ā // JSON.stringify, Object.keys won't expose it ā ā
ā āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā ā
ā ā
ā 3. CONSTANTS - Guaranteed unique identifiers ā
ā āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā ā
ā ā const STATUS = { ā ā
ā ā PENDING: Symbol('pending'), ā ā
ā ā ACTIVE: Symbol('active'), ā ā
ā ā CLOSED: Symbol('closed') ā ā
ā ā }; ā ā
ā ā // Can't accidentally create same value with string typo ā ā
ā āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā ā
ā ā
ā 4. PROTOCOLS/INTERFACES - Define object capabilities ā
ā āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā ā
ā ā const Serializable = Symbol('Serializable'); ā ā
ā ā ā ā
ā ā class User { ā ā
ā ā [Serializable]() { ā ā
ā ā return { id: this.id, name: this.name }; ā ā
ā ā } ā ā
ā ā } ā ā
ā ā ā ā
ā ā function serialize(obj) { ā ā
ā ā if (typeof obj[Serializable] === 'function') { ā ā
ā ā return JSON.stringify(obj[Serializable]()); ā ā
ā ā } ā ā
ā ā } ā ā
ā āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā ā
ā ā
ā 5. METAPROGRAMMING - Customize object behavior with well-known symbols ā
ā ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
š Symbol vs String Keys
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā COMPARISON: SYMBOL VS STRING KEYS ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā¤
ā ā
ā Feature ā String Key ā Symbol Key ā
ā āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā ā
ā Uniqueness ā Can collide ā Always unique ā
ā Enumerable (for-in)ā Yes ā No ā
ā Object.keys() ā Included ā Excluded ā
ā Object.values() ā Included ā Excluded ā
ā JSON.stringify() ā Included ā Excluded ā
ā Object.assign() ā Copied ā Copied ā
ā Spread {...obj} ā Copied ā Copied ā
ā Property access ā obj.key, obj['k']ā obj[symbol] only ā
ā ā
ā Get symbol keys: ā
ā āāā Object.getOwnPropertySymbols(obj) ā
ā āāā Reflect.ownKeys(obj) // Returns both string and symbol keys ā
ā ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā ļø Common Pitfalls
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā SYMBOL PITFALLS ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā¤
ā ā
ā ā Confusing Symbol() with Symbol.for() ā
ā āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā ā
ā Symbol('id') === Symbol('id') // false - different symbols ā
ā Symbol.for('id') === Symbol.for('id') // true - same symbol ā
ā ā
ā ā Using new Symbol() ā
ā āāāāāāāāāāāāāāāāāāāāā ā
ā new Symbol('id'); // TypeError! Symbols are primitives ā
ā Symbol('id'); // ā
Correct way ā
ā ā
ā ā Assuming symbols are truly private ā
ā āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā ā
ā // Symbols can still be accessed: ā
ā Object.getOwnPropertySymbols(obj); ā
ā Reflect.ownKeys(obj); ā
ā // Use # private fields for true privacy ā
ā ā
ā ā Symbol coercion to string ā
ā āāāāāāāāāāāāāāāāāāāāāāāāāāāā ā
ā const sym = Symbol('test'); ā
ā 'Hello ' + sym; // TypeError! Can't convert symbol to string ā
ā `Hello ${sym}`; // TypeError! ā
ā String(sym); // ā
'Symbol(test)' ā
ā sym.toString(); // ā
'Symbol(test)' ā
ā ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
š Summary
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā KEY TAKEAWAYS ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā¤
ā ā
ā ā
Symbols are unique primitive values ā
ā ā
Created with Symbol() or Symbol.for() ā
ā ā
Perfect for unique object keys that won't collide ā
ā ā
Hidden from normal enumeration (for-in, Object.keys, JSON) ā
ā ā
Well-known symbols customize built-in behavior ā
ā ā
Symbol.for() creates global, shareable symbols ā
ā ā
Access with Object.getOwnPropertySymbols() or Reflect.ownKeys() ā
ā ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
š Related Sections
- ā¢Previous: 13.2 Template Literals
- ā¢Next: 13.4 Iterators & Iterables
- ā¢Related: Module 10 - Objects