ES6 Features

Arrow Functions

A short hand notation for function(), but it does not bind this in the same way.

no-eval
var odds = evens.map(v => v + 1);  // no parentes and no brackets
var nums = evens.map((v, i) => v + i);
var pairs = evens.map(v => ({even: v, odd: v + 1}));

// Statement bodies
nums.forEach(v => {
  if (v % 5 === 0)
    fives.push(v);
});

How does this work?

var object = {
    name: "Name", 
    arrowGetName: () => this.name,
    regularGetName: function() { return this.name },
    arrowGetThis: () => this,
    regularGetThis: function() { return this }
}

console.log(this.name)
console.log(object.arrowGetName());
console.log(object.arrowGetThis());
console.log(this)
console.log(object.regularGetName());
console.log(object.regularGetThis());

They work well with classes

class someClass {
    constructor() {
        this.name = "Name"
    }

    testRegular() {
        return function() { return this }

    }

    testArrow() {
        return () => this.name;
    }
}

var obj = new someClass();

console.log(obj.name)
console.log(obj.testRegular()());
console.log(obj.testArrow()());

Classes

As we know them from "real" languages. Syntactic sugar on top of prototype-inheritence.

no-eval
class SkinnedMesh extends THREE.Mesh {
  constructor(geometry, materials) {
    super(geometry, materials);

    this.idMatrix = SkinnedMesh.defaultMatrix();
    this.bones = [];
    this.boneMatrices = [];
    //...
  }
  update(camera) {
    //...
    super.update();
  }
  get boneCount() {
    return this.bones.length;
  }
  set matrixType(matrixType) {
    this.idMatrix = SkinnedMesh[matrixType]();
  }
  static defaultMatrix() {
    return new THREE.Matrix4();
  }
}

Lebab.io

Enhanced Object Literals

var theProtoObj = {
  toString: function() {
    return "The ProtoOBject To string"
  }
}

var handler = () => "handler"


var obj = {
    // __proto__
    __proto__: theProtoObj,

    // Shorthand for ‘handler: handler’
    handler,

    // Methods
    toString() {

     // Super calls
     return "d " + super.toString();
    },

    // Computed (dynamic) property names
    [ "prop_" + (() => 42)() ]: 42
};

console.log(obj.handler)
console.log(obj.handler())
console.log(obj.toString())
console.log(obj.prop_42)

String interpolation

Nice syntax for string interpolation

var name = "Bob", time = "today";

var multiLine = `This

Line

Spans Multiple

Lines`


console.log(`Hello ${name},how are you ${time}?`)
console.log(multiLine)

Destructuring

// list "matching"
var [a, , b] = [1,2,3];
console.log(a)
console.log(b)

Objects can be destructured as well.

nodes = () => { return {op: "a", lhs: "b", rhs: "c"}}
var { op: a, lhs: b , rhs: c } = nodes()
console.log(a)
console.log(b)
console.log(c)

Using Shorthand notation.

nodes = () => { return {lhs: "a", op: "b", rhs: "c"}}

// binds `op`, `lhs` and `rhs` in scope
var {op, lhs, rhs} = nodes()

console.log(op)
console.log(lhs)
console.log(rhs)

Can be used in parameter position


function g({name: x}) {
  return x
}

function m({name}) {
  return name
}

console.log(g({name: 5}))
console.log(m({name: 5}))

Fail-soft destructuring

var [a] = []
var [b = 1] = []
var c = [];
console.log(a)
console.log(b);
console.log(c);

Default

function f(x, y=12) {
  return x + y;
}

console.log(f(3))

Spread

In functions:

function f(x, y, z) {
  return x + y + z;
}
// Pass each elem of array as argument
console.log(f(...[1,2,3]))

In arrays:

var parts = ["shoulders", "knees"];
var lyrics = ["head", ...parts, "and", "toes"]; 

console.log(lyrics)

Spread + Object Literals

We can do cool stuff with this in object creations.

no-eval
let { x, y, ...z } = { x: 1, y: 2, a: 3, b: 4 };
console.log(x); // 1
console.log(y); // 2
console.log(z); // { a: 3, b: 4 }

// Spread properties
let n = { x, y, ...z };
console.log(n); // { x: 1, y: 2, a: 3, b: 4 }
console.log(obj)

Sadly it is not supported yet:

npm install --save-dev babel-plugin-transform-object-rest-spread

Rest

We can allow unlimited params to function by using the rest operator.

function demo(part1, ...part2) {
    return {part1, part2}
}

console.log(demo(1,2,3,4,5,6))

Let

Let is the new var. As it has "sane" bindings.

{
   var globalVar = "from demo1"
}

{
   let globalLet = "from demo2";
}

console.log(globalVar)
console.log(globalLet)

However, it does not assign anything to window:

let me = "go";  // globally scoped
var i = "able"; // globally scoped

console.log(window.me); 
console.log(window.i);

It is not possible to redeclare a variable using let:


let me = "foo";
let me = "bar"; 
console.log(me);

var me = "foo";
var me = "bar"; 
console.log(me)

Const

Const is for read-only variables.

const a = "b"
a = "a"

It should be noted that const objects can still be mutated.

const a = { a: "a" }
a.a = "b"
console.log(a)

For..of

New type of iterator, an alternative to for..in. It returns the values instead of the keys.

let list = [4, 5, 6];

console.log(list)

for (let i in list) {
   console.log(i);
}
let list = [4, 5, 6];

console.log(list)


for (let i of list) {
   console.log(i); 
}

Iterators

The iterator is a more dynamic type than an array.

let infinite = {
  [Symbol.iterator]() {
    let c = 0;
    return {
      next() {
        c++;
        return { done: false, value: c }
      }
    }
  }
}

console.log("start");

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

Using Typescript interfaces we can see how it looks:

no-eval
interface IteratorResult {
  done: boolean;
  value: any;
}
interface Iterator {
  next(): IteratorResult;
}
interface Iterable {
  [Symbol.iterator](): Iterator
}

Generators

Generators create iterators, and are more dynamic than iterators. They do not have to keep track of state in the same manner and do not support the concept of done.

var infinity = {
  [Symbol.iterator]: function*() {
    var c = 1;
    for (;;) {   
      yield c++;
    }
  }
}

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

Using typescript again to show the interfaces.

no-eval
interface Generator extends Iterator {
    next(value?: any): IteratorResult;
    throw(exception: any);
}

function* Iterators and generator

An example of yield*

function* anotherGenerator(i) {
  yield i + 1;
  yield i + 2;
  yield i + 3;
}

function* generator(i) {
  yield i;
  yield* anotherGenerator(i);
  yield i + 10;
}

var gen = generator(10);

console.log(gen.next().value); 
console.log(gen.next().value); 
console.log(gen.next().value); 
console.log(gen.next().value); 
console.log(gen.next().value);

Unicode

ES6 provides better support for Unicode.

var regex = new RegExp('\u{61}', 'u');

console.log(regex.unicode)
console.log("\uD842\uDFD7")
console.log("\uD842\uDFD7".codePointAt())

Modules & Module Loaders

Native support for modules.

no-eval
import defaultMember from "module-name";
import * as name from "module-name";
import { member } from "module-name";
import { member as alias } from "module-name";
import { member1 , member2 } from "module-name";
import { member1 , member2 as alias2 , [...] } from "module-name";
import defaultMember, { member [ , [...] ] } from "module-name";
import defaultMember, * as name from "module-name";
import "module-name";
no-eval
export { name1, name2, …, nameN };
export { variable1 as name1, variable2 as name2, …, nameN };
export let name1, name2, …, nameN; // also var
export let name1 = …, name2 = …, …, nameN; // also var, const

export expression;
export default expression;
export default function (…) { … } // also class, function*
export default function name1(…) { … } // also class, function*
export { name1 as default, … };

export * from …;
export { name1, name2, …, nameN } from …;
export { import1 as name1, import2 as name2, …, nameN } from …;

Import Export

Set

Sets as in the mathematical counterpart where all items are unique. For people who know SQL this is equivalent to distinct.

var set = new Set();
set.add("Potato").add("Tomato").add("Tomato");
console.log(set.size)
console.log(set.has("Tomato"))

for(var item of set) {
   console.log(item)
}

Set

WeakSet

The WeakSet object lets you store weakly held objects in a collection. Objects without a reference will be garbage collected.

var item = { a:"Potato"}
var set = new WeakSet();
set.add({ a:"Potato"}).add(item).add({ a:"Tomato"}).add({ a:"Tomato"});
console.log(set.size)
console.log(set.has({a:"Tomato"}))
console.log(set.has(item))

for(let item of set) {
   console.log(item)
}

WeakSet

Map

Maps, also known as dictionaries.

var map = new Map();
map.set("Potato", 12);
map.set("Tomato", 34);

console.log(map.get("Potato"))


for(let item of map) {
   console.log(item)
}


for(let item in map) {
   console.log(item)
}

Other types than strings can be used.

var map = new Map();
var key = {a: "a"}
map.set(key, 12);


console.log(map.get(key))
console.log(map.get({a: "a"}))

Map

WeakMap

Uses objects for keys, and only keeps weak reference to the keys.

var wm = new WeakMap();

var o1  = {}
var o2  = {}
var o3  = {}


wm.set(o1, 1);
wm.set(o2, 2);
wm.set(o3, {a: "a"});
wm.set({}, 4);

console.log(wm.get(o2));
console.log(wm.has({}))

delete o2;

console.log(wm.get(o3));

for(let item in wm) {
   console.log(item)
}


for(let item of wm) {
   console.log(item)
}

WeakMap

Proxies

Proxies can be used to alter objects' behaviour. They allow us to define traps.


var obj = function ProfanityGenerator() {
    return {
       words: "Horrible words"    
    }
}()

var handler = function CensoringHandler() {
        return {
        get: function (target, key) {
            return target[key].replace("Horrible", "Nice");
        },
    }

}()

var proxy = new Proxy(obj, handler);

console.log(proxy.words);

The following traps are available:

no-eval
var handler =
{
  get:...,
  set:...,
  has:...,
  deleteProperty:...,
  apply:...,
  construct:...,
  getOwnPropertyDescriptor:...,
  defineProperty:...,
  getPrototypeOf:...,
  setPrototypeOf:...,
  enumerate:...,
  ownKeys:...,
  preventExtensions:...,
  isExtensible:...
}

Proxies

Symbols

Symbols are a new type. Can be used to create anonymous properties.

var typeSymbol = Symbol("type");

class Pet {

  constructor(type) {

    this[typeSymbol] = type;

  }
  getType() {
     return this[typeSymbol];
  }

}


var a = new Pet("dog");
console.log(a.getType());
console.log(Object.getOwnPropertyNames(a))


console.log(Symbol("a") === Symbol("a"))

More info

Inheritable Built-ins

We can now inherit from native classes.


class CustomArray extends Array {

}

var a = new CustomArray();

a[0] = 2
console.log(a[0])

It is not possible to override the getter function without using Proxies of arrays.

New Library

Various new methods and constants.

console.log(Number.EPSILON)
console.log(Number.isInteger(Infinity))
console.log(Number.isNaN("NaN"))

console.log(Math.acosh(3))
console.log(Math.hypot(3, 4))
console.log(Math.imul(Math.pow(2, 32) - 1, Math.pow(2, 32) - 2))

console.log("abcde".includes("cd") )
console.log("abc".repeat(3) )


console.log(Array.of(1, 2, 3) )
console.log([0, 0, 0].fill(7, 1) )
console.log([1, 2, 3].find(x => x == 3) )
console.log([1, 2, 3].findIndex(x => x == 2)) 
console.log([1, 2, 3, 4, 5].copyWithin(3, 0)) 
console.log(["a", "b", "c"].entries() )
console.log(["a", "b", "c"].keys() )
console.log(["a", "b", "c"].values() )

console.log(Object.assign({}, { origin: new Point(0,0) }))

Documentation: Number, Math, Array.from, Array.of, Array.prototype.copyWithin, Object.assign

Binary and Octal

Literals for binary and octal numbering.

console.log(0b11111)
console.log(0o2342)

console.log(0xff); // also in es5

Promises

The bread and butter for async programing.

var p1 = new Promise((resolve, reject) => {
  setTimeout(() => resolve("1"), 101)
})
var p2 = new Promise((resolve, reject) => {
  setTimeout(() => resolve("2"), 100)
})

Promise.race([p1, p2]).then((res) => {
   console.log(res)
})

Promise.all([p1, p2]).then((res) => {
   console.log(res)
})

Quick Promise

Need a quick always resolved promise?


var p1 = Promise.resolve("1")
var p2 = Promise.reject("2")

Promise.race([p1, p2]).then((res) => {
   console.log(res)
})

Fail fast

If a promise fails, all and race will reject as well.

var p1 = new Promise((resolve, reject) => {
  setTimeout(() => resolve("1"), 1001)
})
var p2 = new Promise((resolve, reject) => {
  setTimeout(() => reject("2"), 1)
})

Promise.race([p1, p2]).then((res) => {
   console.log("success" + res)
}, res => {
   console.log("error " + res)
})

Promise.all([p1, p2]).then((res) => {
   console.log("success" + res)
}, res => {
   console.log("error " + res)
})

More Info

Reflect

New type of meta programming with new API for existing and also a few new methods.


var z = {w: "Super Hello"}
var y = {x: "hello", __proto__: z};

console.log(Reflect.getOwnPropertyDescriptor(y, "x"));
console.log(Reflect.has(y, "w"));
console.log(Reflect.ownKeys(y, "w"));

console.log(Reflect.has(y, "x"));
console.log(Reflect.deleteProperty(y,"x"))
console.log(Reflect.has(y, "x"));

More Info

Tail Call Optimization

ES6 should fix ensure tail calls do not generate stack overflow. (Not all implementations work).


function factorial(n, acc = 1) {
    if (n <= 1) return acc; 
    return factorial(n - 1, n * acc);
}
console.log(factorial(10))
console.log(factorial(100))
console.log(factorial(1000))
console.log(factorial(10000))
console.log(factorial(100000))
console.log(factorial(1000000))