Symbols in ES6

Symbols are a new primitive type in ES6. If you ask me, they’re an awful lot like strings. Just like with numbers and strings, symbols also come with their accompanying Symbol wrapper object.
We can create our own Symbols.

var mystery = Symbol()

Note that there was no new. The new operator even throws a TypeError when we try it on Symbol.

var oops = new Symbol()
// <- TypeError

For debugging purposes, you can describe symbols.

var mystery = Symbol('this is a descriptive description')

Symbols at quick glance:

  • A new primitive type in ES6
  • You can create your own symbols using var symbol = Symbol()
  • You can add a description for debugging purposes, like Symbol('ponyfoo')
  • Symbols are immutable and unique. Symbol(), Symbol(), Symbol('foo') and Symbol('foo') are all different
  • Symbols are of type symbol, thus: typeof Symbol() === 'symbol'
  • You can also create global symbols with Symbol.for(key)
    • If a symbol with the provided key already existed, you get that one back
    • Otherwise, a new symbol is created, using key as its description as well
    • Symbol.keyFor(symbol) is the inverse function, taking a symbol and returning its key
    • Global symbols are as global as it gets, or cross-realm. Single registry used to look up these symbols across the runtime
      • window context
      • eval context
      • <iframe> context, Symbol.for('foo') === iframe.contentWindow.Symbol.for('foo')
  • There’s also “well-known” symbols
    • Not on the global registry, accessible through Symbol[name], e.g: Symbol.iterator
    • Cross-realm, meaning Symbol.iterator === iframe.contentWindow.Symbol.iterator
    • Used by specification to define protocols, such as the iterable protocol over Symbol.iterator
    • They’re not actually well-known — in colloquial terms
  • Iterating over symbol properties is hard, but not impossible and definitely not private
    • Symbols are hidden to all pre-ES6 “reflection” methods
    • Symbols are accessible through Object.getOwnPropertySymbols
    • You won’t stumble upon them but you will find them if actively looking

Arrow Functions in ES6

Arrow functions are available to many other modern languages and was one of the features I sorely missed a few years ago when I moved from C# to JavaScript. Fortunately, they’re now part of ES6 and thus available to us in JavaScript. The syntax is quite expressive. We already had anonymous functions, but sometimes it’s nice to have a terse alternative.
Here’s how the syntax looks like if we have a single argument and just want to return the results for an expression.

[1, 2, 3].map(num => num * 2)
// <- [2, 4, 6]

The ES5 equivalent would be as below.

[1, 2, 3].map(function (num) { return num * 2 })
// <- [2, 4, 6]

If we need to declare more arguments (or no arguments), we’ll have to use parenthesis.

[1, 2, 3, 4].map((num, index) => num * 2 + index)
// <- [2, 5, 8, 11]

A cool aspect of arrow functions in ES6 is that they’re bound to their lexical scope. That means that you can say goodbye to var self = this and similar hacks – such as using .bind(this) to preserve the context from within deeply nested methods.

function Timer () {
    this.seconds = 0
    setInterval(() => this.seconds++, 1000)
var timer = new Timer()
setTimeout(() => console.log(timer.seconds), 3100)
// <- 3
  • Terse way to declare a function like param => returnValue
  • Useful when doing functional stuff like [1, 2].map(x => x * 2)
  • Several flavors are available, might take you some getting used to
    • p1 => expr is okay for a single parameter
    • p1 => expr has an implicit return statement for the provided expr expression
    • To return an object implicitly, wrap it in parenthesis () => ({ foo: 'bar' }) or you’ll get an error
    • Parenthesis are demanded when you have zero, two, or more parameters, () => expr or (p1, p2) => expr
    • Brackets in the right-hand side represent a code block that can have multiple statements, () => {}
    • When using a code block, there’s no implicit return, you’ll have to provide it — () => { return 'foo' }
  • You can’t name arrow functions statically, but runtimes are now much better at inferring names for most methods
  • Arrow functions are bound to their lexical scope
    • this is the same this context as in the parent scope
    • this can’t be modified with .call, .apply, or similar “reflection”-type methods
    • arguments is also lexically scoped to the nearest normal function; use (...args) for local arguments
  • Read ES6 Arrow Functions in Depth