The road to Angular 2 - ES6 part 1

If you’re reading this, we can be pretty sure you have heard of JavaScript. What we call JS is one implementation of a standard specification, called ECMAScript. The spec version you know the most about is the version 5, that has been used these last years.

But recently, a new version of the spec has been in the works, called ECMAScript 6, ES6, or ECMAScript 2015. From now on, I’ll mainly say ES6, as it is the most popular way to reference it. It adds A LOT of things to JavaScript, like classes, constants, arrow functions, generators… It has so much stuff that we can’t go through all of it, as this blog post would be too long.

But Angular2 has been designed to take advantage of the brand new version of JavaScript. And, even if you can still use your old JavaScript, things will be more awesome if you use ES6. So we’re going to spend some time in this post to get a grip on what ES6 is, and what will be useful to us when building an Angular app.

All of this, and much more, will be included in our upcoming ebook on Angular.

That means we’re going to leave a lot of stuff aside, and we won’t be exhaustive on the rest, but it will be a great starting point. If you don’t know ES6, you will learn some pretty amazing things that will be useful to you even if you end up not using Angular in the future!

Transpilers

ES6 has just reached its final state, so it’s not yet fully supported by every browser. And, of course, some browsers will always be late to this game (do I really need to name it?). You might be thinking: what’s the use of all this, if I need to be careful on what I can use? And you’d be right, because there aren’t that many apps that can afford to ignore older browsers. But, since virtually every JS developer who has tried ES6 wants to write ES6 apps, the community has found a solution: a transpiler.

A transpiler takes ES6 source code and generates ES5 code that can run in every browser. It even generates the source map files, which allows to debug directly the ES6 source code from the browser. At the time of writing, there are two main alternatives to transpile ES6 code:

  • Traceur, a Google project
  • Babeljs, a project started by a young developer, Sebastian McKenzie (17 years old at the time, yeah, that hurts me too), with a lot of diverse contributions.

Each has its own pros and cons. For example, Babeljs produces a more readable source code than Traceur. But Traceur is a Google project, so, of course, Angular and Traceur play well together. The source code of Angular2 itself was at first transpiled with Traceur, before switching to TypeScript.

Let’s be honest Babel has waaaay more steam than Traceur, so I would advice you to use it. It is quickly becoming the de-facto standard in this area.

So if you want to play with ES6, or set it up in one of your projects, take a look at these transpilers, and add a build step to your process. It will take your ES6 source files and generate the equivalent ES5 code. It works very well but, of course, some of the new features are quite hard or impossible to transform in ES5, as they just did not exist. However, the current state is largely good enough for us to use without worrying, so let’s have a look at all these shiny new things we can do in JavaScript!

let

If you have been writing JS for some time, you know that the var declaration is tricky. In pretty much any other languages, a variable is declared where the declaration is done. But in JS, there is a concept, called “hoisting”, which actually declares a variable at the top of the function, even if you declared it later.

So declaring a variable like name in the if block:

function getPonyFullName(pony) {
  if (pony.isChampion) {
    var name = 'Champion ' + pony.name;
    return name;
  }
  return pony.name;
}

is equivalent to declaring it at the top of the function:

function getPonyFullName(pony) {
  var name;
  if (pony.isChampion) {
    name = 'Champion ' + pony.name;
    return name;
  }
  // name is still accessible here,
  // and can have a value from the if block
  return pony.name;
}

ES6 introduces a new keyword for variable declaration, let, behaving much more like what you would expect:

function getPonyFullName(pony) {
  if (pony.isChampion) {
    let name = 'Champion ' + pony.name;
    return name;
  }
  // name is not accessible here
  return pony.name;
}

The variable name is now restricted to its block. let has been introduced to replace var in the long run, so you can pretty much drop the good old var keyword and start using let instead. The cool thing is, it should be painless to use let, and if you can’t, you have probably spotted something wrong with your code!

Constants

Since we are on the topic of new keywords and variables, there is another one that can be of interest. ES6 introduces const to declare… constants! When you declare a variable with const, it has to be initialized and you can’t assign another value later.

const PONIES_IN_RACE = 6;

PONIES_IN_RACE = 7; // SyntaxError

I used a snake-case, full caps, to name the constant, as it’s done in Java. There is no obligation to do so, but it feels natural to have a convention for constants: find yours and stick to it!

As for variables declared with let, constants are not hoisted and are only declared at the block level.

One small thing might surprise you: you can initialize a constant with an object and later modify the object content.

const PONY = { };
PONY.color = 'blue'; // works

But you can’t assign another object:

const PONY = { };
PONY = {color: 'blue'}; // SyntaxError

Same thing with arrays:

const PONIES = [];
PONIES.push({ color: 'blue' }); // works
PONIES = []; // SyntaxError

Creating objects

Not a new keyword, but it can also catch your attention when reading ES6 code. There is now a shortcut for creating objects, when the object property you want to create has the same name as the variable used as the value.

Example:

function createPony() {
  let name = 'Rainbow Dash';
  let color = 'blue';
  return { name: name, color: color };
}

can be simplified to

function createPony() {
  let name = 'Rainbow Dash';
  let color = 'blue';
  return { name, color };
}

Destructuring assignment

This new feature can also catch your attention when reading ES6 code. There is now a shortcut for assigning variables from objects or arrays.

In ES5:

var httpOptions = { timeout: 2000, isCache: true };
// later
var httpTimeout = httpOptions.timeout;
var httpCache = httpOptions.isCache;

Now, in ES6, you can do:

let httpOptions = { timeout: 2000, isCache: true };
// later
let { timeout: httpTimeout, isCache: httpCache } = httpOptions;

And you will have the same result. It can be a little disturbing, as the key is the property to look for into the object and the value is the variable to assign. But it works great! Even better: if the variable you want to assign has the same name as the property, you can simply write:

let httpOptions = { timeout: 2000, isCache: true };
// later
let { timeout, isCache } = httpOptions;

The cool thing is that it also works with nested objects:

let httpOptions = { timeout: 2000, cache: { age: 2 } };
// later
let { cache: { age } } = httpOptions;

And the same is possible with arrays:

let timeouts = [1000, 2000, 3000];
// later
let [shortTimeout, mediumTimeout] = timeouts;
// you now have a variable named 'shortTimeout' with value 1000
// and a variable named 'mediumTimeout' with value 2000

Of course it also works for arrays in arrays, or arrays in objects, etc…

One interesting use of this can be for multiple return values. Imagine a function randomPonyInRace that returns a pony and its position in a race.

function randomPonyInRace() {
  let pony = { name: 'Rainbow Dash' };
  let position = 2;
  // ...
  return { pony, position };
}
let { position, pony } = randomPonyInRace();

The new destructuring feature is assigning the position returned by the method to the position variable, and the pony to the pony variable! And if you don’t care about the position, you can write:

function randomPonyInRace() {
  let pony = { name: 'Rainbow Dash' };
  let position = 2;
  // ...
  return { pony, position };
}
let { pony } = randomPonyInRace();

And you will only have the pony!

Default parameters and values

One of the characteristics of JavaScript is that it allows developers to call a function with any number of arguments:

  • if you pass more arguments than the number of the parameters, the extra arguments are ignored (well, you can still use them with the special arguments variable, to be accurate).
  • if you pass less arguments than the number of the parameters, the missing parameter will be set to undefined.

The last case is the one that is the most relevant to us. Usually, we pass less arguments when the parameters are optional, like in the following example:

function getPonies(size, page) {
  size = size || 10;
  page = page || 1;
  // ...
  server.get(size, page);
}

The optional parameters usually have a default value. The OR operator will return the right operand if the left one is undefined, as will be the case if the parameter was not provided (to be completely accurate, if it is falsy, i.e 0, false, "", etc.). Using this trick, the function getPonies can then be called:

getPonies(20, 2);
getPonies(); // same as getPonies(10, 1);
getPonies(15); // same as getPonies(15, 1);

This worked alright, but it was not really obvious that the parameters were optional ones with default values, without reading the function body. ES6 introduces a more precise way to have default parameters, directly in the function definition:

function getPonies(size = 10, page = 1) {
  // ...
  server.get(size, page);
}

Now it is perfectly clear that the size parameter will be 10 and the page parameter will be 1 if not provided.

NOTE: There is a small difference though, as now 0 or "" are valid values and will not be replaced by the default one, as size = size || 10 would have done. It will be more like size = size === undefined ? 10: size;.

The default value can also be a function call:

function getPonies(size = defaultSize(), page = 1) {
  // the defaultSize method will be called if size is not provided
  // ...
  server.get(size, page);
}

or even other variables, either global variables, or other parameters of the function:

function getPonies(size = defaultSize(), page = size - 1) {
  // if page is not provided, it will be set to the value
  // of the size parameter minus one.
  // ...
  server.get(size, page);
}

Note that if you try to access parameters on the right, their value is always undefined:

function getPonies(size = page, page = 1) {
  // size will always be undefined, as the page parameter is on its right.
  server.get(size, page);
}

This mechanism for parameters can also be applied to values, for example when using a destructuring assignment:

let { timeout = 1000 } = httpOptions;
// you now have a variable named 'timeout',
// with the value of 'httpOptions.timeout' if it exists
// or 1000 if not

Rest operator

ES6 introduces a new syntax to define variable parameters in functions. As said in the previous part, you could always pass extra arguments to a function and get them with the special arguments variable. So you could have done something like that:

function addPonies(ponies) {
  for (var i = 0; i < arguments.length; i++) {
    poniesInRace.push(arguments[i]);
  }
}
addPonies('Rainbow Dash', 'Pinkie Pie');

But I think we can agree that it’s neither pretty nor obvious: since the ponies parameter is never used, how do we know that we can pass several ponies?

ES6 gives us a way better syntax, using the rest operator ...:

function addPonies(...ponies) {
  for (let pony of ponies) {
    poniesInRace.push(pony);
  }
}

ponies is now a true array on which we can iterate. The for ... of loop used for iteration is also a new feature in ES6. It allows to be sure to iterate over the collection values, and not also on its properties as for ... in would do. Don’t you think our code is prettier and more obvious now?

The rest operator can also work when destructuring data:

let [winner, ...losers] = poniesInRace;

The rest operator is not to be confused with the spread operator which, I’ll give you that, looks awfully similar! But the spread operator is the opposite: it takes an array and spreads it in variable arguments. The only examples I have in mind are functions like min or max, that receive variable arguments, and that you might want to call on an array:

let ponyPrices = [12, 3, 4];
let minPrice = Math.min(...ponyPrices);

Classes

One of the most emblematic new features, and one that we will vastly use when writing an Angular app: ES6 introduces classes to JavaScript! You can now easily use classes and inheritance in JavaScript. You always could, using prototypal inheritance, but that it was not an easy task, especially for beginners.

Now it’s very easy, take a look:

class Pony {
  constructor(color) {
    this.color = color;
  }
  toString() {
    return `${this.color} pony`;
    // see that? It is another cool feature of ES6, called template literals
    // we'll talk about them in the next post!
  }
}
let bluePony = new Pony('blue');
console.log(bluePony.toString()); // blue pony

Class declarations, unlike function declarations, are not hoisted, so you need to declare a class before using it. You may have noticed the special function constructor. It is the function being called when we create a new pony, with the new operator. Here it needs a color, and we create a new Pony instance with the color set to “blue”. A class can also have methods, callable on an instance, as the method toString() here.

It can also have static attributes and methods:

class Pony {
  static defaultSpeed() {
    return 10;
  }
}

Static methods can be called only on the class directly:

let speed = Pony.defaultSpeed();

A class can have getters and setters, if you want to hook on these operations:

class Pony {
  get color() {
    console.log('get color');
    return this._color;
  }
  set color(newColor) {
    console.log(`set color ${newColor}`);
    this._color = newColor;
  }
}
let pony = new Pony();
pony.color = 'red'; // set color red
console.log(pony.color); // get color

And, of course, if you have classes, you also have inheritance out of the box in ES6.

class Animal {
  speed() {
    return 10;
  }
}
class Pony extends Animal {

}
let pony = new Pony();
console.log(pony.speed()); // 10, as Pony inherits the parent method

Animal is called the base class, and Pony the derived class. As you can see, the derived class has the methods of the base class. It can also override them:

class Animal {
  speed() {
    return 10;
  }
}
class Pony extends Animal {
  speed() {
    return super.speed() + 10;
  }
}
let pony = new Pony();
console.log(pony.speed()); // 20, as Pony overrides the parent method

As you can see, the keyword super allows calling the method of the base class, with super.speed() for example.

The super keyword can also be used in constructors, to call the base class constructor:

class Animal {
  constructor(speed) {
    this.speed = speed;
  }
}
class Pony extends Animal {
  constructor(speed, color) {
    super(speed);
    this.color = color;
  }
}
let pony = new Pony(20, 'blue');
console.log(pony.speed); // 20

Enjoying it? Our next part is now available, with promises, arrow functions, new collections, modules and more!



blog comments powered by Disqus