Symbols

Med

Symbol is JavaScript's 7th primitive type, introduced in ES6. Every Symbol() call creates a completely unique value — even two symbols with the same description are different. Symbols serve as collision-free property keys and enable metaprogramming through well-known symbols like Symbol.iterator and Symbol.toPrimitive.

Interactive Visualization

Built-in Data Structures

"age":30

Key Points

  • Symbol() creates a guaranteed-unique value every time — no two symbols are ever equal
  • Symbol.for(key) uses a global registry — same key returns the same symbol across realms
  • Well-known symbols (Symbol.iterator, Symbol.toPrimitive, Symbol.hasInstance) customize language behavior
  • Symbols as property keys are not enumerable by for...in or Object.keys()
  • Symbol.description provides a read-only description string for debugging
  • Symbols cannot be implicitly converted to strings — must use .toString() or .description

Code Examples

Creating and Using Symbols

const sym1 = Symbol('id')
const sym2 = Symbol('id')
console.log(sym1 === sym2) // false — always unique!

const user = {
  name: 'Alice',
  [sym1]: 'hidden-id-001',
}

console.log(user[sym1])        // 'hidden-id-001'
console.log(Object.keys(user)) // ['name']
console.log(Object.getOwnPropertySymbols(user)) // [Symbol(id)]

Symbols are unique identifiers perfect for property keys that should not collide with string keys.

Symbol.for() Global Registry

const s1 = Symbol.for('app.id')
const s2 = Symbol.for('app.id')
console.log(s1 === s2) // true — same key, same symbol!

console.log(Symbol.keyFor(s1)) // 'app.id'

const s3 = Symbol('app.id')
console.log(s1 === s3) // false — not in registry

Symbol.for() enables sharing symbols across files and realms. Regular Symbol() always creates a new unique value.

Well-Known Symbol: Symbol.toPrimitive

class Temperature {
  constructor(celsius) { this.celsius = celsius }

  [Symbol.toPrimitive](hint) {
    switch (hint) {
      case 'number': return this.celsius
      case 'string': return `${this.celsius}°C`
      case 'default': return this.celsius
    }
  }
}

const temp = new Temperature(36.6)
console.log(+temp)            // 36.6 (number)
console.log(`Body: ${temp}`) // 'Body: 36.6°C' (string)
console.log(temp > 37)        // false (number)

Symbol.toPrimitive lets you control how an object converts to primitive values in different contexts.

Symbol.hasInstance

class EvenNumber {
  static [Symbol.hasInstance](num) {
    return typeof num === 'number' && num % 2 === 0
  }
}

console.log(2 instanceof EvenNumber)   // true
console.log(3 instanceof EvenNumber)   // false
console.log(100 instanceof EvenNumber) // true

Symbol.hasInstance customizes instanceof, enabling duck-type checking.

Common Mistakes

  • Using Symbol() with new — Symbol is not a constructor, new Symbol() throws
  • Expecting symbols to appear in for...in or Object.keys()
  • Confusing Symbol() with Symbol.for() — former is always unique, latter uses shared registry
  • Trying to concatenate symbols with strings implicitly — throws TypeError

Interview Tips

  • Symbols solve the name collision problem for object properties
  • Know at least 3 well-known symbols: Symbol.iterator, Symbol.toPrimitive, Symbol.hasInstance
  • Understand Symbol() (always unique) vs Symbol.for() (shared registry)
  • JSON.stringify ignores symbol-keyed properties

Related Concepts