ES6 New Features

Arrows

Arrows are a function shorthand using the => syntax (‘fat arrow’). They are syntactically similar to the related feature in C#, Java 8 and CoffeeScript. They support both expression and statement bodies. Unlike functions, arrows share the same lexical this as their surrounding code.

Scope

The lexical scope (this) in arrow is same as surrounding scope, unlike functions which create new scope.

/* Lexical scope (this) in arrow is same as surrounding scope,
   but function scope is a new scope */

var pets = ['cat', 'dog', 'bird', 'fish'];

var a = pets.map(p => p.length);

console.log(a);

Classes

ES6 classes are simply syntactical sugar over the prototype-based Object Orientation pattern. Having a single convenient declarative form makes class patterns easier to use. Classes support prototype-based inheritance, super calls, instance and static methods and constructors.

class Vehicle {
  name = 'Vehicle';

  constructor(doors, wheels) {
    this.doors = doors;
    this.wheels = wheels;
    this.engineState = 'stopped';
    console.log('Contructed a vehicle with '
      + this.doors + ' doors and '
      + this.wheels + ' wheels.');
  }
  startEngine() {
    this.engineState = 'started';
    console.log('Engine started ..');
  }
  stopEngine() {
    this.engineState = 'stopped';
    console.log('Engine stopped ..');
  }
  drive() {
    if(this.engineState == 'started') {
      console.log('driving ..');
    } else {
      console.log('start the engine first!');
    }
  }
  static showName() {
    console.log(this.name);
  }
  static get instance() {
    if(!Vehicle) {
      return new Vehicle();
    }
    return this;
  }
}

class Car extends Vehicle {
  name = 'car';

  constructor(doors, wheels) {
    super(doors, wheels);
  }
  static showName() {
    console.log(this.name);
  }
}

Vehicle.instance.showName();

var car = new Car(2, 4);
car.startEngine();

Let & Const

They are Block-scoped binding constructs. let has block scope unlike var which has lexical scope.

function run() {
  let it = "be";

  // block scope
  {
    let it = "go";
    console.log(it);  // go
  }
  console.log(it);  // be
}

run();

Single Assignment

The constant const can only be assigned once and immutable. It prevents use before assignment.

function run() {
  const it = "be";

  it = "go"; // error: trying to override 'it' constant

  console.log(it);
}

run();

Symbols

Symbols enable access control for object state. A symbol is a unique and immutable data type and may be used as an identifier for object properties.
Symbols are a new primitive type. Optional name parameter used in debugging – but is not part of identity. Symbols are unique, but not private since they are exposed via reflection features like Object.getOwnPropertySymbols.

var count = Symbol('count');

class People {
    constructor() {
        this[count] = 0;
    }

    add(item) {
        this[this[count]] = item;
        this[count]++;
    }

    static sizeOf(instance) {
        return instance[count];
    }

}

var people = new People();

console.log(Symbol("count") === Symbol("count")); // false
console.log(People.sizeOf(people)); // 0

people.add('John Smith');

console.log(People.sizeOf(people)); // 1

Generators

Generators are functions that can be paused(exit) and resumed(re-enter).
Their context (variable bindings) will be saved across re-entrances.
These can be helpful for iterators, asynchronous programming, etc.

Execution

Calling a generator function does not execute its body immediately; an iterator object for the function is returned instead.
When the iterator’s next() method is called, the generator function’s body is executed until the first yield expression.

var fib = fibonacci();

function* fibonacci(){
  let val1 = 0,
      val2 = 1,
      swap;

  yield val1;
  yield val2;

  while (true){
    swap = val1 + val2;
    val1 = val2;
    val2 = swap;

    yield swap;
  }
}

function run(){
  let i = 0;

  while ( i < 10 ) {
    console.log(fib.next().value);
    i++;
  }
}

run();

Iterators & For..of

Iterator enables JavaScript objects to define custom iteration like CLR IEnumerable or Java Iterable.
This will generalize for..in to custom iterator-based iteration with for..of.

@@iterator

In order to be iterable, an object must implement the @@iterator method, meaning that the object (or one of the objects up its prototype chain) must have a property with a Symbol.iterator.

var foo = "foo",
    iter1, iter2, iter3, iter4,
    iterator;

iterator = foo[Symbol.iterator]();

iter1 = iterator.next();
iter2 = iterator.next();
iter3 = iterator.next();
iter4 = iterator.next();

console.log(iter1); // done: false, value: "f"
console.log(iter2); // done: false, value: "o"
console.log(iter3); // done: false, value: "o"
console.log(iter4); // done: true, value: undefined

The for…of statement creates a loop Iterating over iterable objects (including Array, Map, Set, arguments object and so on),
invoking a custom iteration hook with statements to be executed for the value of each distinct property.

var fib = fibonacci();

function* fibonacci(){
  let val1 = 0,
      val2 = 1,
      swap;

  yield val1;
  yield val2;

  while (true){
    swap = val1 + val2;
    val1 = val2;
    val2 = swap;

    yield swap;
  }
}

for (var n of fib) {
  // truncate the sequence at 1000
  if (n > 1000)
    break;
  console.log(n);
}

run(); // 1 1 2 3 5 8 13 21 34 55 89 144 233

Promises

Promises are a library for asynchronous programming. Promises are a first class representation of a value that may be made available in the future.
Promises are used in many existing JavaScript libraries.

ES6 Promise API

The de-facto standard for JavaScript promises is called Promises/A+. ES6 Promise API follows this standard.

Promises are a pattern that helps with asynchronous programming, functions that return their results asynchronously.

function timeout(duration = 0) {
    return new Promise((resolve, reject) => {
      setTimeout(resolve, duration);
      console.log('Timed out.. ' + duration);
    })
}

var p = timeout(1000).then(() => {
    return timeout(2000);
}).then(() => {
    throw new Error("uh oh!");
}).catch(err => {
    return Promise.all([timeout(100), timeout(200)]);
});

Destructuring

The destructuring assignment syntax is a JavaScript expression that makes it possible to extract data from arrays or objects using a syntax that mirrors the construction of array and object literals.

Syntax

[a, b] = [1, 2]
[a, b, …rest] = [1, 2, 3, 4, 5]
{a, b} = {a:1, b:2}

This capability is similar to features present in languages such as Perl and Python.

var fruits = ["apple", "orange", "banana"];

/* without destructuring
var apple   = fruits[0];
var orange   = fruits[1];
var banana = fruits[2];
*/

// with destructuring
var [apple, orange, banana] = fruits;

console.log(apple); // "apple"
console.log([orange, banana]);// ["orange", "banana"]

Modules

A module is a self-contained unit of code, which is usually stored in a file. In ES6, modules get language-level support for component definition.

There are two popular for JavaScript module loaders.

  • CommonJS: Used heavily in Node.js

    • Syntactically compact
    • Loads synchronously
    • Used by the server
  • AMD: Used by RequireJS

    • Syntactically complex. Enables AMD to work without compilation.
    • Loads asynchronously
    • Used by the browser
Import & Export

You have to use the export keyword to explicitly make it available. And use import keyword to use the module.

libs/math.js
export function sum(x, y) {
  return x + y;
}
export var pi = 3.141593;
app.js
import * as math from "lib/math";

alert("2π = " + math.sum(math.pi, math.pi));

Math + Number + String + Object APIs

ES6 has many new library additions, including core Math libraries, Array conversion helpers, and Object.assign for copying.

Number.EPSILON; // fixes decimal fractions, rounding errors
Number.isInteger(Infinity); // false
Number.isFinite(Infinity) // false
Number.isNaN("NaN"); // false
Number.isSafeInteger(Infinity); // false

Math.acosh(3); // 1.762747174039086
Math.hypot(3, 4); // 5
Math.imul(Math.pow(2, 32) - 1, Math.pow(2, 32) - 2); // 2

"abcde".includes("cd"); // true
"abc".repeat(3); // "abcabcabc"

var obj = { a: 1 };
var copy = Object.assign({}, obj);
console.log(copy); // { a: 1 }
Global functions

Four number-related functions are already available as global functions and have been added to Number, as methods: isFinite, isNaN, parseFloat and parseInt.

The Object.assign() method only copies enumerable and own properties from a source object to a target object.
It uses [[Get]] on the source and [[Put]] on the target, so it will invoke getters and setters.
Therefore it assigns properties versus just copying or defining new properties.

Exceptions on Object

Note that Object.assign() does not throw on null or undefined source values.

Default + Rest + Spread

ES6 gives us a way to set default function parameters. Any parameters with a default value are considered to be optional.

function sum(number, increment = 1) {
  return number + increment;
}

sum(2, 2); // 4
sum(2);    // 3

Rest parameters are indicated by three dots preceding a parameter. This named parameter becomes an array which contains the rest of the parameters.

function sum(...numbers) {
  var result = 0;
  numbers.forEach(function (number) {
    result += number;
  });
  return result;
}
sum(1); // 1
sum(1, 2, 3, 4, 5); // 15

The spread is closely related to rest parameters, because of … (three dots) notation. It allows to split an array to single arguments which are passed to the function as separate arguments.

function sum(a, b, c) {
  return a + b + c;
}

var args = [1, 2, 3];
sum(...args); // 6

Instead using an apply function, we can just type …args and pass all array argument separately.

Proxies

In ES6 proxies enable you to intercept and customize operations performed on objects including accessing properties. Can be used for interception, object virtualization, logging/profiling, etc.

// Proxying a normal object
var target = {};
var handler = {
  get: function (receiver, name) {
    return 'Hello, ' + name;
  }
};

var p = new Proxy(target, handler);
p.world === "Hello, world!";

// Proxying a function object
var target = function () { return "I am the target"; };
var handler = {
  apply: function (receiver, ...args) {
    return "I am the proxy";
  }
};

var p = new Proxy(target, handler);
p() === "I am the proxy";
Methods of handler object

The handler object is a placeholder object which contains traps for Proxy.

Traps

All traps are optional. If a trap has not been defined, the default behavior is to forward the operation to the target.

  • getPrototypeOf()
  • setPrototypeOf()
  • isExtensible()
  • preventExtensions()
  • getOwnPropertyDescriptor()
  • defineProperty()
  • has()
  • get()
  • set()
  • deleteProperty()
  • enumerate()
  • ownKeys()
  • apply()
  • apply()

More new features will be discussed in ES6 Advanced Features post.

Be first to comment

Leave a Reply