I know I might be a little behind on this post, but I’ve been working exclusively on the front end at my new job and as a result I’ve been much more involved with JavaScript. We are using Angular.js and as part of our strategy of future-proofing (as much as you can for the front end) our application, we are using patterns likely to be used in Angular 2.0, which will use ES6 and Javascript Next functionality extensively. I’ve had to learn a lot of ES6 basics to effectively work with it and Angular together, and this article will go over what I think are the ES6 basics everyone should know.

ES6?

ES6 is the latest standard for ECMAScript, which is the standard that JavaScript is based on. The international standards body Ecma International is responsible for further developing the JavaScript specification and bringing more features to the language. ES6 brings classes and the super keyword, the let keyword, default parameters, the fat arrow => function syntax, a module and export system and many other more advanced language features to JavaScript.

All code written for Angular 2.0 is being written entirely in ES6 and with AtScript, Google’s JavaScript superset [EDIT 2015-07-12: Angular 2 is now being written in TypeScript], but if you don’t want to use that it will play perfectly well with ES6.

Traceur

Since most browsers do not yet fully support ES6, it is necessary to run all ES6 code through a compiler to convert it to ES5, which is the current version of JavaScript. Google’s Traceur compiler is one of the best of these, and being supported by Google and used in their development processes for Angular 2.0, you can be sure it’s not going anywhere anytime soon.

There are many different ways that Traceur can be used, either from the command lines or via one of the myriad build system plugins like gulp-traceur, grunt-traceur and broccoli-traceur. You will be able to find one for your build workflow of choice.

Another extremely popular transpiler is Babel, which is used a lot in the React community.

Classes

Classes are the first main addition and ES6 basic feature that I’m going to tell to you about. Traditionally, classes were not available to JavaScript developers because JavaScript is an object-based language with a prototypical inheritance system. What this means is that all objects, except object literals, inherit from other objects through the prototype property.

This is best demonstrated through a contrived Person example. Here is how it might play out using ES5 syntax, with every individual inheriting from a base Person function.

function Person(name, age, gender) {
    this.name = name !== undefined ? name : 'John';
    this.age = age !== undefined ? age : 0;
    this.gender = gender !== undefined ? gender : '?';
}
Person.prototype.greet = function () {
    alert('Hello ' + this.name + '!');
}

var John = new Person('John', 21, 'M');
console.log(John.greet()); // Hello John!;

Classes are, at the end of the day, just syntactic sugar over the conventional functional inheritance system that developers are used to. But the addition of powerful features like constructors, superclass accessors, private fields and functions makes it feel like you are working with a class-based language like C# or Java.

Here is the same Person implemented again, this time using ES6 classes instead of the ES5 syntax:

class Person {
    constructor(name = 'John', age = 0, gender = '?') {
        this.name = name;
        this.age = age;
        this.gender = gender;
    }

    greet() {
        alert('Hello ' + this.name + '!');
    }
}

var John = new Person('John', 21, 'M');
console.log(John.greet()); // Hello John!;

As you can see, the prototype keyword is nowhere to be seen, and the syntax is much more familiar to those coming from class-based languages. Here is an example of how you can extend a class and use the super accessor to still set up the base class:

class Man extends Person {
    constructor(name = 'John', age = 0) {
        super(name, age, 'M');
    }
}

Extends works the same way as inheritance does in class-based languages, and methods can be overridden in the child class!

Class Functions & Default Parameters

As you might have noticed in the above examples, there are a few changes to functions in ES6. The new function declaration syntax within classes drops the function keyword, so instead of writing:

Person.prototype.greet = function greet() {
    alert('Hello!');
}

You would just write this within your class:

greet() {
    alert('Hello!');
}

In addition to this new function syntax, JavaScript finally gets default parameters. This is opposed to the usual method of using default values in JavaScript, which basically just checks whether the value is falsey (false, null, undefined, "") and then setting the value to the default, like so:

function addProduct(name, price, type) {
    name = name || 'New Product';
    price = price || 0.0;
    type = type || 'Uncategorized';
}

If you need to be able to pass in falsey values, then the following undefined check syntax would usually be used:

function addProduct(name, price, type) {
    name = typeof name !== undefined ? name : 'New Product';
    price = typeof price !== undefined ? price : 0.0;
    type = typeof type !== undefined ? type : 'Uncategorized';
}

You can do the same thing in the function definition with ES6 by using default parameters, making the whole thing more succinct.

addProduct(name = 'New Product', price = 0.0, type = 'Uncategorized') {
  api.products.add({ name, price, type });
}

The Fat Arrow

If you are familiar with CoffeeScript, C# or other languages that use the fat arrow (=>) for lambda expressions, then the way that they are used in ES6 will be familiar to you. With ES6 there is even less of a reason to use CoffeeScript with the addition of the ‘fat arrow’ syntax for anonymous functions. This is mainly useful for callback and lambda functions, for example when a promise is returned.

getPeople().then((people) => {
    for (let i = 0; i < people.length; i++) {
        console.log(people[i].name);
    }
});

If only one argument is passed with the function, the parenthesis can be omitted, and if no arguments are passed then just empty parenthesis () are used. One of the most exciting things about the new fat arrow syntax is that the this keyword is lexically bound to the enclosing context, which means that the parent function’s this keyword is used. Consider the following example and how it differs from ES5 (examples used from MDN):

ES5

function Person() {
  this.age = 0;

  setInterval(function birthday() {
      // this.age above remains 0
      this.age++; // this == global window in ES5.
  }, 1000);

  console.log(age) // still 0!
}

ES6

function Person() {
    this.age = 0;

    setInterval(() => {
        this.age++; // this properly refers to the Person object
    }, 1000);

    console.log(age) // very, very old!
}

The let and const Keyword

The let keyword is new in ES6, and it allows you to define variables within a function that are scoped to a block, statement or expression. Usually variables are declared globally within a function, which can lead to some issues if you are not aware of this. An example is required for this to make some more sense:

Without Let

function test() {
    var x = 10;

    if (true) {
      var x = 20;
      console.log(x); // outputs 20
    }

    console.log(x); // outputs 20
}

With Let

function test() {
    var x = 10;

    if (true) {
        let x = 20;
        console.log(x); // outputs 20
    }

    console.log(x); // outputs 10 as expected
}

You also cannot define the same variable using the let keyword twice, because this will result in a TypeError thrown at runtime. One other place where let is handy is with for loops, so the loop variable is completely isolated from the rest of the function variables. Here is how a for loop looks using let.

  for(let i = 0; i < arr.length; i++) {
      console.log(arr[i]);
  }

Have a look at the MDN article on let for more examples and more in-depth explanations.

Another new keyword is the const keyword, which allows for read-only constant variables. The only important things to note is that a value must be assigned when the variable is created, so const API_SERVER; will not work. As well as this, you cannot reassign the value of the const, though it will not error but rather fail silently.

Finally you can use object literals with const, and the value reassignment will fail silently as above. The const declaration does not however prevent property values from being modified.

As with let you should look at the article for const over at MDN.

Modules & Exports

Up until now, JavaScript has not had a robust, native dependency management or module system, giving rise to the CommonJS and AMD module syntax, which libraries like Browserify and RequireJS take advantage of, and Node.js uses the CommonJS module syntax. It is absolutely essential in this modern age of Single Page Applications and Web Apps to keep your JavaScript code in modular parts for easier testing, development and organisation. So a native implementation of modules was a natural step forward for JavaScript.

Here is a basic example of how modules can be used. Functions, objects and variables can be exported using the syntax, as can classes:

// a set of global variables in globals.js
export const API_ENDPOINT = 'http://api.app.com/'
export const API_VERSION  = 1;

// importing them in app.js. Note that .js can be omitted
import { API_ENDPOINT, API_VERSION } from 'globals';

Or if an entire class needs to be exported, you can use the default keyword, which allows for only one export per module.

// class in person.js
class Person {
    constructor() {
        this.age = 0;
        this.gender = 'Unknown';
    }
}
export default Person;

// import in app.js
import Person from 'person';

You can then, using Traceur, define the module loader output that you would like to use (CommonJS, AMD) or even Instantiate for SystemJS loading, or all of your modules loaded inline for one file. There are a few different options for the Traceur output, make sure you check them out! As well as this, the AngularJS 2.0 team are said to be ditching modules in favour of using the ES6 module system, so it’s also handy to start using them now if you are using Angular!

You can see more information about the final ES6 module syntax at http://www.2ality.com/2014/09/es6-modules-final.html.

Object Literal Shorthand

One more tiny thing as well, the object literal definition now has a shorthand for keys that share the same name as defined variables. So instead of writing:

var obj = {
    data: data,
    values: values
}

You can just write this:

var obj = { data, values}

Pretty neat!

Go Nuts!

ES6 is the future of JavaScript, so I urge you to use these ES6 basics when developing new projects, or even when rebuilding parts of existing projects. The ES6 is changing constantly but finally starting to stabilize, and you should have no problems using it until the spec becomes standard across browsers. As long as you use a compiler like Traceur everything will be interoperable, and you’ll be able to take advantage of the new ES6 language features today!