You can make symbols out of pretty much anything.
const str = 'foo'
const num = 1
const bool = true
const arr = []
const obj = {}
const date = new Date()
const err = new Error()
// create two unique symbols from the same string registered in memory
const a = Symbol(str)
const aa = Symbol(str)
// create two unique symbols from the same number registered in memory
const b = Symbol(num)
const bb = Symbol(num)
// create two unique symbols from the same boolean registered in memory
const c = Symbol(bool)
const cc = Symbol(bool)
// construct two unique symbols from the same array registered in memory
const d = Symbol(arr)
const dd = Symbol(arr)
// construct two unique symbols from the same object registered in memory
const e = Symbol(obj)
const ee = Symbol(obj)
// construct two unique symbols from the same Date instance registered in memory
const f = Symbol(date)
const ff = Symbol(date)
// construct two unique symbols from the same Error instance registered in memory
const g = Symbol(err)
const gg = Symbol(err)
console.log(a === aa) // false
console.log(b === bb) // false
console.log(c === cc) // false
console.log(d === dd) // false
console.log(e === ee) // false
console.log(f === ff) // false
console.log(g === gg) // false
console.log(h === hh) // false
Checking for equality on each pair of Symbols that were made from the same variable registered in memory, each is false since Symbols are globally unique.
Oddly enough, you can also create Symbols from built-in objects:
const h = Symbol(Object)
const hh = Symbol(Object)
const i = Symbol(Array)
const ii = Symbol(Array)
const j = Symbol(Function)
const jj = Symbol(Function)
console.log(h === hh) // false
console.log(i === ii) // false
console.log(j === jj) // false
What if these aren't actually creating Symbols, and we're getting false
on all of these equality checks because of some silent failure? Can we somehow check for truthiness to verify we're creating Symbols? Yes!
If we use Symbol's built-in for
method, we can declare a Symbol into the global Symbol registry if it doesn't already exist, but if it does exist, the global registry will return the already declared Symbol.
const h = Symbol.for(Object) // Symbol from Object doesn't yet exist. Let's register one
const hh = Symbol.for(Object) // A Symbol from Object was already registered. Return it
const i = Symbol.for(Array)
const ii = Symbol.for(Array)
const j = Symbol.for(Function)
const jj = Symbol.for(Function)
console.log(h === hh) // true
console.log(i === ii) // true
console.log(j === jj) // true
Now all of our pairs are true, because the first Symbol declaration checks for a Symbol made from the given object, which doesn't yet exist, and so it creates a new Symbol from the object.
When the second Symbol declaration checks for a Symbol made from the given object, it was already registered, and for()
returns that previously registered Symbol.
Taking this a step further, if we declare a third Symbol in each pair without using for()
, then that Symbol will be unique since there is no registry lookup when creating that Symbol:
const h = Symbol.for(Object)
const hh = Symbol.for(Object)
const hhh = Symbol(Object) // doesn't perform a registry lookup, so it creates a new Symbol
const i = Symbol.for(Array)
const ii = Symbol.for(Array)
const iii = Symbol(Array)
const j = Symbol.for(Function)
const jj = Symbol.for(Function)
const jjj = Symbol(Function)
console.log(h === hh) // true
console.log(hh === hhh) // false
console.log(i === ii) // true
console.log(ii === iii) // false
console.log(j === jj) // true
console.log(jj === jjj) // false