Standard ECMA-262, 6th edition (June 2015)

ECMAScript 6 has been finalised under Standard ECMA-262 6th edition (June 2015). There's no going back now! The JavaScript community can breathe a sigh of relief before gearing up for the next major language revision. Or will we all live in the future using transpilers? Let's take a minute to look at ECMAScript 2015's language features.

Declarations

JavaScript's scoping rules are difficult to understand because var behaves inconsistently depending on scope. The new keywords, let (MDN) and const (MDN), scope based on lexical environment:

Usually a Lexical Environment is associated with some specific syntactic structure of ECMAScript code such as a FunctionDeclaration, a BlockStatement, or a Catch clause of a TryStatement and a new Lexical Environment is created each time such code is evaluated.

So, not only do you get the extra expressive power of being able to declare constants, but you also get the ability to declare variables according to more natural scoping rules. Basically: let works the way most of us thought var did at some point when we learned JavaScript.

Here's something you can try in a Chrome debugger to see how this works:

(function() {
  'use strict'
  for (let i = 0; i < 3; i++) {
    console.log('loop:', i);
  }
  console.log('after loop:', typeof i);
})();

The last statement will show that i is undefined after the loop. Now try this:

(function() {
  'use strict'
  for (var i = 0; i < 3; i++) {
    console.log('loop:', i);
  }
  console.log('after loop:', typeof i);
})();

You should see that i still exists after the loop. Almost everyone I've ever explained this to has forgotten it within a week. The new wisdom is this: you probably want const! If not let. Let's forget about var and everything will be OK.

Arrow Functions

If you think it's weird that for doesn't exist in the same dimension as var, then what about this in anonymous methods? It's very easy to make the mistake of passing a function to a method and assuming this will be bound to the object that owns the method. What really happens is this becomes the global object.

Arrow functions (MDN) help avoid unbound weirdo global this and also makes anonymous functions more readable:

setTimeout(() => { console.log('Hello'); }, 1000);  

Arrow functions may omit the curly braces when arguments are supplied. Too many callbacks? Do I care when I can just type =>?

Generators

Generator functions (MDN) are ideal for creating functions that should be exited and continued later. They can make asynchronous APIs arguably cleaner, and are starting to be adopted by API designers for things that would be typically callback heavy, like database APIs.

Generator objects are both iterator and iterable: there's a whole section in the spec on iteration, but rather than puzzling over that if you want a solid technical introduction to iteration see Dr. Axel Rauschmayer's Iterables and iterators in ECMAScript 6.

Class syntax

Now you can make classes (MDN) the way you do in other languages. Kind of. Classes support subclassing (extend), and in derived classes you have to call super. Did you know that underneath the minimalist sheen of ES6 classes you'll find good old fashioned prototype chains?

The design of ES6 classes is mostly backwards compatible with existing code, so you don't need to rush around rewriting prototype classes as ES6 classes. However, like arrow functions and the new block scoping, using ES6 classes reduces boilerplate. You'll appreciate that fact when subclassing (there's no goofy constructor function .call, just call super).

Default and Rest Parameters

Speaking of boilerplate, did you know ES6 has default parameters (MDN) and rest (MDN) parameters?

You can now set defaults when defining arguments:

function f(a, b=123, c='a') { console.log(a, b, c); }  
f('a!');  

And here's a "rest" example:

function multiply(multiplier, ...theArgs) {  
  return theArgs.map(function (element) {
    return multiplier * element;
  });
}

var arr = multiply(2, 1, 2, 3);  
console.log(arr); // [2, 4, 6]  

More

Naturally there's a lot more that I don't have time to cover today: maps, weak maps, destructuring, modules, promises, proxies, typed arrays, template strings.

But of course, we're all using promises already! Template strings: grown up interpolation, extremely useful for everything from SQL statements to generating client-side HTML fragments.

Typed arrays relate to JavaScript evolving as a language for dealing with binary data: people have already been using and abusing them for making things like games, synthesisers, and graphics processing.

I saw this great tweet by Eric Elliott (retweeted by Marc Harter, my coauthor on Node.js in Practice) that linked the ES6 announcement with the news about WebAssembly:

WebAssembly, "wasm" for short, .wasm filename suffix, a new binary syntax for low-level safe code, initially co-expressive with asm.js, but in the long run able to diverge from JS's semantics, in order to best serve as common object-level format for multiple source-level programming languages.

It almost feels like JavaScript has changed overnight, except for the fact that ECMAScript 2015 has taken many years of hard work! It could have been more radical, but the standardisation process has kept it largely conservative and backwards compatible. It should also help us solve some of the issues JavaScript has as a language.

Now, if only those Node/io.js guys could get things all merged up so we can start using more ES6 features without having to transpile Node code...

Leave a Reply

Your email address will not be published. Required fields are marked *