Learn more about Russian war crimes in Ukraine.

What are symbols in JavaScript?

I stumbled across “symbols” when reading about JavaScript iterators. Here’s what I saw:

const myIterable = {};
myIterable[Symbol.iterator] = 
  () => { /* ... */ };

An iterable has the unusual property Symbol.iterator, but what on earth is Symbol.iterator? According to Node:

$ node
> Symbol.iterator
> typeof Symbol.iterator

Apparently Symbol.iterator has the type symbol. If you learned JavaScript before 2015, this type didn’t exist. It seems that symbol is the only new JavaScript type that has been added since JavaScript was created! The others are object, function, string, number, boolean, and undefined.

Why was it so important to create a fundamental new data type? Wouldn’t myIterable.iterator = ... be enough? No: this could clash with an existing property on the object called iterator. By introducing the new symbol type, the JavaScript designers were able to introduce myIterable[Symbol.iterator] without the possibility of this clashing with existing properties.

You can create your own symbols with the Symbol function:

$ node
> Symbol("foo")
> typeof Symbol("foo")

Behind the scenes, there is a symbol registry. You can think of this registry as an array of strings:

const registry = ["Symbol.iterator", "foo"]

Every time you call Symbol(description), it pushes the new string description to the array, and gives you back a new symbol that contains the index of the latest string the registry. So, given the registry above, Symbol("bar") would modify the registry to ["Symbol.iterator", "foo", "bar"], then return a symbol containing the index 2.

Given a symbol, you can get the description that was passed in using .description. The expression s.description effectively returns registry[s]:

$ node
> x = Symbol("foo")
> x.description
> y = Symbol()
> y.description
> Symbol.iterator.description

So far, you could simulate symbols yourself using an array, just like above - so why do they have their own new type? Symbols are useful because of what you can’t do with them. Given a symbol, you cannot get access to the symbol’s registry index. The only things you can do with a symbol are compare it to something else, or look it up in an object (or get the string description - an unusual operation).

Symbol comparison uses the symbols’ registry indexes; not the string descriptions! Try it out:

$ node
> foo1 = Symbol("foo")
> foo2 = Symbol("foo")
> foo1 === foo2
> foo1 === foo1

The above example added two new "foo" descriptions to the registry, so it would look like the following:

const registry = [..., "foo", "foo"]

So far, I’ve referred to “the registry”, but actually there are many. First, there is one registry per “JavaScript realm”. In the browser, this basically means one per page/tab. This is the registry you’re modifying when creating symbols with Symbol("foo"). But there is an additional table, the “global symbol registry”, which is shared between multiple realms. (Just how “global” it is seems to be undefined.)

The global registry is accessed with Symbol.for, which acts a bit like Symbol:

$ node
> foo1 = Symbol.for("foo")
> foo2 = Symbol.for("foo")
> foo3 = Symbol("foo")
> foo1 === foo2
> foo1 === foo3

Notice that Symbol.for does not create a new symbol every time. If a symbol already exists with that description in the global registry, it will return that symbol. This means that, unlike the per-realm registries, the global registry has unique symbol descriptions.

So, actually, a symbol is not just an index. It also carries along with it a reference to the registry that it’s a member of. You can’t easily tell, given a symbol, which registry it’s a member of. But you can exploit what we know to determine whether a symbol is a member of the global registry:

> isGlobalSymbol = (s) => Symbol.for(s.description) === s
[Function: isGlobalSymbol]
> isGlobalSymbol(foo1)
> isGlobalSymbol(foo2)
> isGlobalSymbol(foo3)

What can computers do? What are the limits of mathematics? And just how busy can a busy beaver be? This year, I’m writing Busy Beavers, a unique interactive book on computability theory. You and I will take a practical and modern approach to answering these questions — or at least learning why some questions are unanswerable!

It’s only $19, and you can get 50% off if you find the discount code ... Not quite. Hackers use the console!

After months of secret toil, I and Andrew Carr released Everyday Data Science, a unique interactive online course! You’ll make the perfect glass of lemonade using Thompson sampling. You’ll lose weight with differential equations. And you might just qualify for the Olympics with a bit of statistics!

It’s $29, but you can get 50% off if you find the discount code ... Not quite. Hackers use the console!

More by Jim

Tagged #programming, #javascript. All content copyright James Fisher 2019. This post is not associated with my employer. Found an error? Edit this page.