ECMAscript 6, my favorite new features

I was recently asked about which features of ES6 that most excite me, and in answering, I came to realize that I had not transcended the curiosity that comes from reading about it. That is, I haven't implemented the new experiemental features yet. So before doing so, I figured this would be a good way to review and familiarize my brain and hands with the powers of ES6.

Here's a little dive into some exciting new features.

Inspiration and examples pulled from ES6fiddle and Egghead.io.

let (not var)

var is still a perfectly valid declaration for new variables, but the let keyword introduces some more predictable behavior than that which comes packaged with vanilla javascript.

block scope

The keyword let is not hoisted like var, so a declaration made with let is only created at the time of its declaration.

In respect to block scope, this means that you do not need to be in the scope of a function in order to have different values for the same variable names:

var i = "outside";  
{
    var i = "inside";
}
console.log(i); // "inside"

var i = "outside";  
{
    let i = "inside";
}
console.log(i); // "outside"  
a more useful example

A for loop using var, like we're used to, instantiates a variable declared in the front, and then changes the value on every iteration. This can trip us up pretty easily when we pass our first-class functions by reference, and bind values inside of those functions to the variable. For instance:

var functions = [];

// var keyword declares 'i' once

for (var i = 1; i <= 5; i++){  
  functions.push(function(){
      console.log(i);
  });
}

functions.forEach(function(f){  
    f();
});

yields:

6  
6  
6  
6  
6  

We know to expect this behavior from javascript, but that doesn't mean we have to like it. Because i is declared once, any function that references it should expect its value to equal whatever the value is at the time of the function's invocation. Because we invoke all the functions here after we're done incrementing the same instance of i, each of these functions will reference its final value.

Using the let keyward inside of our for loop can provide us the behavior that we were most likely hoping for:

var functions = [];

// let keyword declares a new 'i' in block scope, each time

for (let i = 1; i <= 5; i++){  
  functions.push(function(){
      console.log(i);
  });
}

functions.forEach(function(f){  
    f();
});

yields:

1  
2  
3  
4  
5  

Nice.

Destructured Assignment

When working with objects with which you are only concerned with particular keys and values, ES6 provides a convenient way of quickly assigning references to those inner keys.

let [one, two] = [1, 2];  
let {three, four} = {three: 3, four:  4};

console.log(one, two, three, four);

yields:

1 2 3 4  

Iterators

I can't be the only guy that, in an itertive loop on more than one unfortunate occasion, referred to the index of an array when my intention was to use the value stored at that index. Well, gone are those days. Behold the of keyword:

var arr = ['A','D','A','M'];  
var result = '';

for (var i of arr){  
    result += i;
};
console.log(result);  

yields:

"ADAM"  

Spread syntax

The spread operator enables us to manipulate values of a collection, as a collection, without having to specifically target them. It works a lot like an each or map method that we would ordinarily pull in via underscore or lodash.

var front = [1,2,3,4,5];  
var back  = [6,7,8,9,10];  
front.push(back)  
console.log(front)

yields:

[1,2,3,4,5,[6,7,8,9,10]]

Of course, but... ick. spread syntax to the rescue!

var front = [1,2,3,4,5];  
var back  = [6,7,8,9,10];  
front.push(...back)  
console.log(front)

yields:

[1,2,3,4,5,6,7,8,9,10]

Default Parameters

Countless times, I have longed for a more semantic way to declare default values for the expected arguments of a function.

var thisThing = thisThing || somethingElse;  

We all know what it means, but that doesn't mean it should be necessary. be able to specify default values in the function declaration is convenient and a familiar feature from myriad other languages. Finally, it's here.

function defaults(x = 3, y = 6){  
    return [x,y];
}

so,

console.log(defaults());  

yields:

[3,6]

and

console.log(defaults(1,1));  

yields:

[1,1]

Lovely.

Generators

A generator is like a function that can be paused and resumed. It can yield a different value each time (like a return, but without finishing), if that's what you'd like. This example from ES6fiddle shows clearly how we can create a range function.

function* range(start, end, step) {  
    while (start < end) {
        yield start;
        start += step;
    }
}

for (let i of range(0, 10, 2)) {  
    console.log(i);
}

yields:

0  
2  
4  
6  
8  

Generators come with a promise-like API, including a .next() method. Calling that method will make your generator step to the next yield declaration. In this example from Egghead.io, you can even go so far as to create an infinite loop in a generator:

function* noStackOverflow(){  
    let x = 0;
    let y = 0;
    while(true){
        yield {x:x, y:y}
        x += 2;
        y += 1;
    }
}
var generator = noStackOverflow();  
console.log(generator.next().value);  
console.log('Do something else');  
console.log(generator.next().value);  

yields:

{x:0,y:0}
Do something else  
{x:2,y:1}

This generator will continue to generate a new object and set of values for us upon every request.

And so much more.

There are many other great features to get excited about that are not covered here. To name a few:

  • built-in support for promises using a Promise constructor
  • ability to instantiate classes via a class declaration
  • function => notation, which carries much of the same functionality with which it comes in Coffeescript.