ECMAScript 6

AGENDA

  1. Overview of ECMAScript
  2. ECMAScript 6 Features
  3. Using ECMAScript 6 today

Overview of ECMAScript

What is ECMAScript?

  • Scripting language specification standardized by ECMA International.

  • Developed by Technical Committee 39 (TC-39).

  • The official name of JavaScript.

Brief History

Brief History

ECMAScript 6 Browser support

Name Version Coverage
IE 11, Edge 13, Edge 14 15%, 79%, 90%
49 93%
52 98%
39 98%
8, 9, 10 21%, 53%, 100%

ECMAScript 5 Pitfalls

  • Prototype inheritance (No class)
  • new, this
  • Function scope (No block scope)
  • Hoisting
  • Global variables (No module system)
  • NaN, undefined
  • typeof null
  • ...

Why ECMAScript 6?

  • Be a better language for writing:
    • Complex applications.
    • Libraries shared by those applications.
    • Code generators targeting the new edition.

ECMAScript 5 vs ECMAScript 6

ECMAScript 6 Features

var

Unexpected value changes are a source of bugs.

var x = 3;
function func(randomize) {
    if (randomize) {
        var x = Math.random();
        return x;
    }
    return x;
}

func(false);

undefined

var x = 3;
function func(randomize) {
    var x;
    if (randomize) {
        x = Math.random();
        return x;
    }
    return x;
}

func(false);

var

var funcs = [];
for (var i = 0; i < 5; i++) {
    funcs.push(function(){
        console.log(i);
    });
}

funcs[3]();
var i;
var funcs = [];
for (i = 0; i < 5; i++) {
    funcs.push(function(){
        console.log(i);
    });
}

funcs[3]();

5

let

  • Block scope
  • Temporal Dead Zone instead of hoisting
var x = 3;
function func(randomize) {
    if (randomize) {
        let x = Math.random();
        return x;
    }
    return x;
}
func(false); //3
var x = 3;
function func(randomize) {
    if (randomize) {
        var x = Math.random();
        return x;
    }
    return x;
}

func(false); //undefined

let

  • Loops created fresh binding of the variable
var funcs = [];
for (var i = 0; i < 5; i++) {
    funcs.push(function(){
        console.log(i);
    });
}

funcs[3](); //5
var funcs = [];
for (let i = 0; i < 5; i++){
    funcs.push(function(){
        console.log(i);
    });
}
funcs[3](); //3

From IIFEs to blocks

(function () { // open IIFE 
    var tmp = ···;
    ···
}()); // close IIFE
console.log(tmp); 
{ // open block
    let tmp = ···;
    ···
} // close block
console.log(tmp);

constant

const foo;//SyntaxError
const a = 2;
console.log(a); //2
a = 3; //Uncaught TypeError
const a = [1, 2, 3];
a.push(4);
a.push(5);
console.log(a);  // [1,2,3,4,5]
a = 42;  //Uncaught TypeError: Assignment to constant variable.

const obj = {Id: 1};
obj.Name = "Walter";
console.log(obj); //Object {Id: 1, Name: "Walter"}
obj = {};//Uncaught TypeError: Assignment to constant variable.

Best Practice

  • Use const by default.
  • Use let if you have to rebind a variable.
  • Avoid var.

From function expressions to arrow functions

var foo = function(x, y){
    return x + y;
};
console.log(foo(1, 3)); //4
let foo = (x, y) => x + y;
console.log(foo(1, 3)); //4
function doSomething() {
    var self = this;
    setTimeout(function() {
        self.start();
    }, 500);
}
function doSomething() {
    setTimeout(() => {
        this.start();
    }, 500);
}

Spread and Rest

Spread operator

let arr = [1, 2, 3];
foo(arr[0], arr[1], arr[2]); // 1 2 3
let arr = [1, 2, 3];
foo( ...arr );// 1 2 3

From apply() to the spread operator (... )

function foo(x, y, z) {
    console.log( x, y, z );
}
let a = [1], b = [2, 3], c = [4];
let result = a.concat(b, c);
console.log(result);//[1,2,3,4]
let a = [1], b = [2, 3], c = [4];
let result = [...a, ...b, ...c];
console.log(result);//[1,2,3,4]

From concat() to the spread operator (... )

let nums = [-1, 11, 3];
Math.max(nums[0], nums[1], nums[2]);
Math.max.apply(null, nums);
let nums = [-1, 11, 3];
Math.max(...nums);// 11

From arguments to rest parameters

function foo() {
    console.log("first:", arguments[0]);
    var rest = Array.prototype.slice.call(arguments, 1);
    console.log("rest:", rest);
}
foo(1, 2, 3, 4, 5);
function foo(first, ...rest) {
    console.log("first:", first);
    console.log("rest:", rest);
}
foo(1, 2, 3, 4, 5);
// first: 1
// rest: [2, 3, 4, 5]

Default Parameter

Default Parameter

Lazily evaluated

function add (x, y){
    x = x || 1;
    y = y || 1;
    console.log(x + y);
}
	
add(0, 4); //5 not 4
function add (x = 1, y = 1){
    console.log(x + y);
}

add(undefined, 6); // 7
add(null, 6);  // 6

=

Destructuring Assignment

ES5 Assignment

var arr = [1,2,3];
var a = arr[0], b = arr[1], c= arr[2];
var obj = {x: 4, y: 5, z: 6 }
var x = obj.x, y = obj.y, z = obj.z;
var obj = {x: 4, y: 5, z: 6 }
var a = obj.x, b = obj.y, c = obj.z;

ES6 Assignment

let [a,b,c] = [1,2,3];
let {x,y,z} = {x: 4, y: 5, z: 6 };
let {x:a, y:b, z:c} = {x:4, y:5, z:6};

Destructuring Assignment

Swap variables

[left, right] = [right, left]

Default values

let [ a = 3, b = 6, c = 9, d = 12 ] = [1, 2, 3];
let { x, y, z, w = 20 } = (() => ({ x: 4, y: 5, z: 6 }))();

Computed property

let key = "z"; let { [key]: foo } = { z: "bar" };

Rest items

let a = [2, 3, 4]; let [b, ... c] = a;

Named parameter

let o = { a: 1, b: 2, c: 3 };
function doSomething({b, a, c} = o){
  console.log(a,b,c);
}

Complex structures

let obj = { x: { y: { z: 6 } } };
let { x: { y: { z: w } } } = obj;

Class

ES5

function ES5Class(name){
    this.name = name;
}
ES5Class.prototype.work = function(){
}

function ES5DerivedClass(name, title){
    ES5Class.call(this, name);
    this.title = title;
}
ES5DerivedClass.prototype = Object.create( ES5Class.prototype);
ES5DerivedClass.prototype.constructor = ES5Class;
ES5DerivedClass.prototype.work = function () {
    ES5Class.prototype.work.call(this); //super call
    // do more...
};

ES6

class ES6Class{
    constructor(name){
        this.name = name;
    }
    work(){
    }
}
class ES6DerivedClass extends ES6Class{
    constructor(name, title){
      super(name);
	 this.title = title;
    }
    work(){
	 super.work();
         //do more….
    }
}

Class

ECMAScript 6 Class

  • Syntactic sugar
  • Constructor, inheritance, static method, getter/setter
  • Mixin - multiple inheritance
  • Super calls
  • Instance
  • Inheriting the built-in natives
  • Class declarations are not hoisted

ECMAScript 6 Class

class Person {
     constructor(name) {
         this._name = name;
     }
     prototypeMethod() {
         console.log(`prototype method`);
     }
     static staticMethod(){
         console.log(`static method`);
     }
     get name() {
         return this._name.toUpperCase();
     }
     set name(newName) {
         this._name = newName;
     }
}
class Employee extends Person {
    constructor(name, title){
        super(name);
        this._title = title;            
    }
    prototypeMethod() {
        super.prototypeMethod();
        console.log(`${this._name}, ${this._title}`);
    }
}

let employee = new Employee("Rapth", "Developer");
employee.prototypeMethod();

Module

ECMAScript 5 Module Pattern

var moduleName = (function () {
    //private state
    //private functions

    return {
        //public state
        //public variables
    };
})();

ECMAScript 5 Module Pattern

var es5Module = (function () {
    var _privateProperty = 'Hello World';
     
    function _privateMethod() {
        console.log(_privateProperty);
    };

    return {
        publicMethod: function() {
            _privateMethod();
        }
    };
})();

Module Export

ECMAScript 5 Module Pattern

var es5Module = (function () {
    var _privateProperty = 'Hello World';
    var publicProperty = 'I am a public property';
  
    function _privateMethod() {
        console.log(_privateProperty);
    }
  
    function publicMethod() {
    	_privateMethod();
    }
     
    return {
        publicMethod: publicMethod,
        publicProperty: publicProperty
    };
})();

Revealing Module Pattern

ECMAScript 5 Module Pattern

var es5Module = (function (myModule) {
    var _privateProperty = 'Hello World';
    var publicProperty = 'I am a public property';
  
    function _privateMethod() {
        console.log(_privateProperty);
    }

    myModule.publicMethod = function(){
        _privateMethod();
    }
     
    return myModule;
})(es5Module || {});

Augment Module

ECMAScript 5 vs ECMAScript 6

var es5Module = (function () {
    function greeting(name) {
        console.log( "Hello " + name);
    }

    return {
        greeting: greeting
    };
})();

es5Module.greeting("Walter");
//es6Module.js
export function greeting(name){
    console.log( "Hello " + name);
}

//app.js
import { greeting } from 'es6Module'
greeting("Walter");

ES5

ES6

ECMAScript 6 Module

  • Native standard module system.
  • File-based, one module per file.
  • Singleton, encapsulation.
  • Static module structure.
  • Support for both synchronous and asynchronous loading.
  • Support for cyclic dependencies between modules.

ECMAScript 6 Module

//module.js
export function foo() {
    console.log("foo");
}
export default function doSomething(){
    console.log("default");
}

let awesome = 42;
let bar = [1, 2, 3];
let baz = {Id: 1, Name: "ABC"};

export { bar, awesome, baz as obj };
//app.js
import fnDefault, {foo as aliasFoo, bar, awesome, obj} from "module"
/* import fnDefault, * as es6Module from "module" */

fnDefault();
aliasFoo();
console.log(bar);
console.log(awesome);
console.log(obj);

Promise

Promise

Callback hell - N levels deep

function foo(finalCallback) {
  request.get(url1, function(err1, res1) {
    if (err1) { return finalCallback(err1); }
    request.post(url2, function(err2, res2) {
      if (err2) { return finalCallback(err2); }
      request.put(url3, function(err3, res3) {
        if (err3) { return finalCallback(err3); }
        request.del(url4, function(err4, res4) {
          // let's stop here
          if (err4) { return finalCallback(err4); }
          finalCallback(null, "whew all done");
        })
      })
    })
  })
}

Promise - 1 level deep

function foo() {
  return request.getAsync(url1)
  .then(function(res1) {
    return request.postAsync(url2);
  }).then(function(res2) {
    return request.putAsync(url3);
  }).then(function(res3) {
     return request.delAsync(url4);
  }).then(function(res4) {
     return "whew all done";
  });
}

Callback vs Promise

Callback Promise
Is function Is object
Is passed as arguments Is returned
Handle success and failure Don’t handle anything
Can represent multiple events Can only represent one event

Promise Advantages

  • Unified handling of both asynchronous errors and normal exceptions.
  • Reuseable.
  • Chaining is easier.

Promise in ECMAScript 6

  • The ECMAScript 6 Promise follows Promises/A+ standard, allow us to work with Promises natively in the language.
  • Libraries that implement Promise/A+: jquery 3.0, Q, When, Bluebird, ...
  • ES6 Promise API: Promise.resolve, Promise.reject, Promise.all, Promise.race, Promise.prototype.catch.
let p = new Promise(function (resolve, reject) {
        setTimeout(() => resolve(4), 2000);
});

p.then((res) => console.log(res)); //4

Bonus

Generator + Promise: 0 level flat

function* foo() {
  var res1 = yield request.getAsync(url1);
  var res2 = yield request.getAsync(url2);
  var res3 = yield request.getAsync(url3);
  var res4 = yield request.getAsync(url4);
  return "whew all done";
}

ECMAScript 6 Performance

Feature ​Comparison with ES5
​Destructuring 2x slower
​Arrow Function Identical
​Default Parameter Identical
​Template Literal 30x slower
Promise (with jquery 3.0) 6x faster
Class Identical

Using ECMAScript 6 today

Using ECMAScript 6 today

  • Current JavaScript engines (browsers, Node.js, …) already support much of ES6.
  • Support for legacy engines: use transpiling (Babel, Google Traceur, ...).
  • ES6 Standard library (Map, Set, new Array methods, ...): use Shim/Polyfill

Babel

JavaScript transpiler

ES6

ES5

const a = [1, 2, 3];
const a1 = input.map(item => item + 1);
"use strict";
var a = [1, 2, 3];
var a1 = input.map(function (item) {
  return item + 1;
});

Babel - Usage

  "devDependencies": {
    "babel-core": "^6.9.1",
    "babel-loader": "^6.2.4",
    "babel-polyfill": "^6.9.1",
    "babel-preset-es2015": "^6.9.0",
    "webpack": "^1.13.1"
  },
  "scripts": {
    "webpack": "webpack --watch"
  },
var path = require('path');
 
module.exports = {
    entry: {
        userManager: "./Scripts/userManager.js"
    },
    output: {
        path: path.resolve(__dirname, './'),
        filename: './Bundle/[name].js'
    },
    module: {
        loaders: [
            {
                test: /\.js$/,
                loader: 'babel-loader',
                query: {
                    presets: ['es2015']
                }
            }
        ]
    },
    devtool: 'source-map'
};

package.json

webpack.config.js

npm run webpack

start

To ECMAScript 7... and beyond

//Async function
async function main() {
    let ret = await step1();
    ret = await step2( ret );
    ret = await Promise.all([
            step3a( ret ),
            step3b( ret ),
            step3c( ret )]);
    await step4( ret );
}
//object.observe(..)
let obj = { a: 1, b: 2 };
Object.observe(obj, function(changes){
        for (var change of changes) {
            console.log( change );
        }
    },
    [ "add" , "update" , "delete"]
);
obj.c = 3; 
// { name: "c", object: obj, type: "add" }
//Exponentiation operator
2 ** 3; //8
//Object property and ...
let o1 = { a: 1, b: 2 }, o2 = { c: 3 };
let o3 = { ... o1, ... o2, d: 4 };
console.log(o3.a, o3.b, o3.c, o3.d);
// 1 2 3 4

//destructured properties
let o1 = { b: 2, c: 3, d: 4 };
let { b, ... o2 } = o1;
console.log(b, o2.c, o2.d);  // 2 3 4
//Array#includes(..)
let vals = [ "foo" , "bar" , 42, "baz" ];
if (vals.includes( 42 )) {
 // found it!
}
//SIMD (Single Instruction Multiple Data)
var a = SIMD.Float32x4(1, 2, 3, 4);
var b = SIMD.Float32x4(5, 6, 7, 8);
var c = SIMD.Float32x4.add(a,b); // Float32x4[6,8,10,12]

References

ECMAScript 6 Presentation

By sakataa

ECMAScript 6 Presentation

Overview of ECMAScript 6

  • 1,529