ES2015

WHAT IS ECMASCRIPT?

May '95

Dec '95

Sep '95

1997

Aug '96

1999

1998

2009

2003

2015

2011

2016

Mocha  (Brendan Eich, Netscape)

LiveScript

JavaScript

Edition 1

JScript  (Microsoft)

Edition 2

Edition 3

Edition 4

Edition 5

Edition 5.1

Edition 6

Edition 7

ECMA-262 specification

  • Latest finalised version ECMAScript6 (ES6, ES2015, Harmony)
  • Change to yearly naming convention (ES7 = ES2016)

What is ES6?

  • ECMAScript 6, also known as ECMAScript 2015, is the upcoming version of the ECMAScript standard.
  • This standard is targeting ratification in June 2015.
  • ES6 is a significant update to the language, and the first update to the language since ES5 was standardized in 2009.

COMPATIBILITY

TRANSPILATION

  • Transpiler compiles code latest version -> older version
  • ES5 widely supported by browsers -> ES6 should be compiled to ES5 for compatibility
  • Two main ES6 -> ES5 transpilers:
    • Traceur
    • Babel
  • Babel generally preferred:
    • More readable transpiled code
    • JSX support (compatibility with React.js)
  • Alternatively use higher-level language implementing ES6 features
    • CoffeeScript
    • Dart
    • Typescript (more later)

Transpilation

/*jshint esnext: true */

import Todo from './todo';
import {values} from './generators';

class TodoList {
  constructor() {
    this.todos = {}; 
    this.length = 0;
  }

  add(text, done = false) {
    let todo = new Todo(text, done);
    this.todos[String(todo.timestamp)] = todo;
    this.length++;
  }

  archiveCompleted() {
    for (let todo of values(this.todos)) {
      if (todo.done) this.delete(todo);
    }
  }

  delete(todo) {
    delete this.todos[String(todo.timestamp)];
    this.length--;
  }

  getUncompletedCount() {
    let count = 0;
    for (let todo of values(this.todos)) {
      if (!todo.done) count++;
    }
    return count;
  }
}

export default TodoList;
/*jshint esnext: true */

'use strict';

Object.defineProperty(exports, '__esModule', {
  value: true
});

var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }

var _todo = require('./todo');

var _todo2 = _interopRequireDefault(_todo);

var _generators = require('./generators');

var TodoList = (function () {
  function TodoList() {
    _classCallCheck(this, TodoList);

    this.todos = {};
    this.length = 0;
  }

  _createClass(TodoList, [{
    key: 'add',
    value: function add(text) {
      var done = arguments.length <= 1 || arguments[1] === undefined ? false : arguments[1];

      var todo = new _todo2['default'](text, done);
      this.todos[String(todo.timestamp)] = todo;
      this.length++;
    }
  }, {
    key: 'archiveCompleted',
    value: function archiveCompleted() {
      var _iteratorNormalCompletion = true;
      var _didIteratorError = false;
      var _iteratorError = undefined;

      try {
        for (var _iterator = (0, _generators.values)(this.todos)[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
          var todo = _step.value;

          if (todo.done) this['delete'](todo);
        }
      } catch (err) {
        _didIteratorError = true;
        _iteratorError = err;
      } finally {
        try {
          if (!_iteratorNormalCompletion && _iterator['return']) {
            _iterator['return']();
          }
        } finally {
          if (_didIteratorError) {
            throw _iteratorError;
          }
        }
      }
    }
  }, {
    key: 'delete',
    value: function _delete(todo) {
      delete this.todos[String(todo.timestamp)];
      this.length--;
    }
  }, {
    key: 'getUncompletedCount',
    value: function getUncompletedCount() {
      var count = 0;
      var _iteratorNormalCompletion2 = true;
      var _didIteratorError2 = false;
      var _iteratorError2 = undefined;

      try {
        for (var _iterator2 = (0, _generators.values)(this.todos)[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
          var todo = _step2.value;

          if (!todo.done) count++;
        }
      } catch (err) {
        _didIteratorError2 = true;
        _iteratorError2 = err;
      } finally {
        try {
          if (!_iteratorNormalCompletion2 && _iterator2['return']) {
            _iterator2['return']();
          }
        } finally {
          if (_didIteratorError2) {
            throw _iteratorError2;
          }
        }
      }

      return count;
    }
  }]);

  return TodoList;
})();

exports['default'] = TodoList;
module.exports = exports['default'];

ES6

ES5

Class

  • ES5 objects inherit from Object.protoype 
  • Can instantiate and define methods using the protoype
function Client (firstname, lastname) {
  this.id = undefined
  this.firstname = firstname
  this.lastname = lastname
}

Client.prototype.getFullName = function () {
    return this.firstname + this.lastname
}

Client.prototype.setId = function () {
    this.id = generateGuid()
}

ES5

ES6

class Client {

    constructor(firstname, lastname) {
        this.id = undefined
        this.firstname = firstname
        this.lastname = lastname
    }

    getFullName() {
        return this.firstname + this.lastname
    }

    generateId() {
        this.id = generateGuid()
    }

}
  • ES6 classes are 'syntactic sugar' on top of prototypal inheritance

Class

ES6

class Client {

    constructor(firstname, lastname) {
        this.id = undefined
        this.firstname = firstname
        this.lastname = lastname
    }

    getFullName() {
        return this.firstname + this.lastname
    }

    generateId() {
        this.id = generateGuid()
    }

}
  • Method signature notation - shorter, cleaner
  • No commas between properties, methods

STATIC METHODS

ES6

class Client {

    constructor(firstname, lastname) {
        this.id = undefined
        this.firstname = firstname
        this.lastname = lastname
    }

    getFullName() {
        return this.firstname + this.lastname
    }

    generateId() {
        this.id = generateGuid()
    }

    static generateGuid() {
        function s4() {
            return Math.floor((1 + Math.random()) * 0x10000)
            .toString(16)
            .substring(1)
        }
        
        return s4() + s4() + '-' + s4() + '-' + s4() + '-' +
        s4() + '-' + s4() + s4() + s4()
    }
    
}
  • Can't be called on an instantiated class
  • No access to 'this' properties/methods
  • Most useful when associated with class, but don't need unique variables of instanced class

 

INHERITANCE

ES6 introduces the 'extend' keyword

class TCSClient extends Client {

    this.evalSystem = "TCS"

}
var TCSClient = Object.create(Client);

TCSClient.evalSystem = "TCS"

ES6

ES5

Can override functions by re-implementing them in child class

class TCSClient extends Client {

    getFullName() {
        return this.firstname + " " +
            this.lastname
    }

}
var TCSClient = Object.create(Client)

TCSClient.prototype.getFullName() {
    return this.firstname + " " +
        this.lastname
}

ES5

ES6

Super()

ES5

function Client () {
    this.vcNumber = undefined;
    this.vcNumberPrefix = "";
}

Client.prototype.generateVCNumber = function () {
    var number = Math.floor(Math.random() * 10000000);
    this.vcNumber = this.vcNumberPrefix + pad(number, 7);
}

var TCSClient = Object.create(Client);

TCSClient.vcNumberPrefix = "A";

TCSClient.prototype.generateVCNumber = function() {
    Client.prototype.generateVCNumber.call(this);
}

Can access methods of the parent object using super()

ES6

class DRClient extends Client {

    this.vcNumberPrefix = "W";

    generateVCNumber() {
        super.generateVCNumber();
    }

}

Modules

  • Modules is a file that exports an API
  • Implemented in ES6 as standard
  • Exported bindings are available to other modules that import them
  • Emulated in libraries such as Common JS, AMD before ES6

Default export

Only one default export per module

// ClientService.js

class ClientService {
    
    clients = [];

    addClient(client) {
        this.clients.add(client);
    }

}

export default ClientService;
import ClientService from './ClientService';

class ClientComponent {

    submitClient(client) {

        ClientService.addClient(client);

    }

}

Named exports

  • Many exports per module
  • Can use with default export
  • Can be expressed as a list
// Constants.js

var $BANKRUPTCY_MIN_DEBT = 1001;
var $SEQUESTRATION_MIN_DEBT = 3000;

export $BANKRUPTCY_MIN_DEBT;
export $SEQUESTRATION_MIN_DEBT;
// Constants.js

var $BANKRUPTCY_MIN_DEBT = 1001;
var $SEQUESTRATION_MIN_DEBT = 3000;

export { $BANKRUPTCY_MIN_DEBT,
    $SEQUESTRATION_MIN_DEBT}

or

Importing named exports

// Constants.js

var $BANKRUPTCY_MIN_DEBT = 1001;
var $SEQUESTRATION_MIN_DEBT = 3000;

export $BANKRUPTCY_MIN_DEBT;
export $SEQUESTRATION_MIN_DEBT;
import {$BANKRUPTCY_MIN_DEBT,
    $SEQUESTRATION_MIN_DEBT} from './Constants';

function checkSomething(
    return $BANKRUPTCY_MIN_DEBT > $SEQUESTRATION_MIN_DEBT;
}

rename when importing

// Constants.js

var $BANKRUPTCY_MIN_DEBT = 1001;
var $SEQUESTRATION_MIN_DEBT = 3000;

export $BANKRUPTCY_MIN_DEBT;
export $SEQUESTRATION_MIN_DEBT;
import {$BANKRUPTCY_MIN_DEBT as minDebt,
    $SEQUESTRATION_MIN_DEBT as seq} from './Constants';

function checkSomething(
    return minDebt > seq;     
}

Importing the entire module

  • A namespace import must have an alias
  • Any default export would be placed in alias.default
// IncomesController.js

export var incomeSupport;
export var jobSeekers;
export var incapacity;

export function totalBenefitIncome () {
    this.valIncomeSupport +
        this.valJobSeekers +
        this.valIncapacity;
}
import * as incomes from './IncomesController'

function submitIncomes() {

    incomes.incomeSupport = this.valIncomeSupport;
    incomes.jobseekers = this.valJobseekers;
    incomes.incapacity = this.valIncapacity;
    
    this.valTotalBenefitIncome = incomes.TotalBenefitIncome;

}

Symbol

New primitive type

Similar to strings:

  • Immutable
  • Can't add properties on them (as with any primitive)
  • Can be used as a property key
var mySymbol = Symbol("description");

console.log(typeof mySymbol);    // 'symbol'

Unique (even where description is same)

Symbol('foo') === Symbol('foo');    // false

Does not use 'new' operator

var symbol = new Symbol();    // TypeError

Symbol

Symbol

  • Avoiding name clashes in property keys - e.g.
  • Creates a unique property in scope - no collisions
if (element.isMoving) {
  smoothAnimations(element);
}
element.isMoving = true;
var isMoving = Symbol("isMoving");

...

if (element[isMoving]) {
  smoothAnimations(element);
}
element[isMoving] = true;
var myArray = ['a', 'b', 'c'];

var myIterable = myArray[Symbol.iterator]();
> iterable.next()
{ value: 'a', done: false };
> iterable.next()
{ value: 'b', done: false };
> iterable.next()
{ value: 'c', done: false };
> iterable.next()
{ value: undefined, done: true };

Symbol

Can flag an object as having a set of common methods by attaching certain symbols, e.g.

Iterable objects use the 'well-known' Symbol.iterator

  • myArray has Symbol.iterator property:
    • each item in collection has 'value' and 'done' properties
    • can iterate through list with next()

ITERATORS

  • ES6 introduces iterator and iterable protocols
  • 'Iterability' involves data sources and data consumers

ITERATORS

  • Data consumers:
    • for - of loops
    • Array.from
    • spread operator (more later)
  • Data sources: some built-ins are iterable by default in ES6
    • Arrays
    • Strings
    • Maps
    • Sets
    • DOM data structures (e.g. querySelectorAll method)
  • To comply with the iterator protocol:
    • Object must have a next() method
    • The next() method takes no arguments
  • The next() method returns an object with at least:
    • A 'done' property: true (sequence done), or false (may be more values)
    • A 'value' property: current item in sequence

ITERATORS

  • The Symbol.iterator is used to assign an 'iterator' to any object
  • Symbol.iterator equates to a method that must return an object that complies with the iterator protocol.
[Symbol.iterator]: () => ({
    
    items: ['f', 'o', 'o'],

    next: function next() {

        return {
            done: this.items.length === 0,
            value: this.items.shift()
        }
    }
})

next() method (no args)

returns Object with done, value

ITERATORS

function* myGenerator() {}

// or

function *myGenerator() {}

GENERATORS

  • Special kind of iterator
  • 'Generator function' declared, using asterisk notation:

Generator functions contain 'yield' expressions

  • On 'yield' expression, function:
    • Suspends execution
    • Emits a value
function* myGenerator() {

    yield 'f';
    console.log('o');
    yield 'o';
    console.log('b');
    yield 'a';
    console.log('r');

}
var fooBar = myGenerator();

for(var item of fooBar) {

    console.log(item);        // 'f' 'o' 'o' 'b' 'a' 'r'

}

GENERATORS

function *myGenerator(x) {
    
    var y = x + 3;
    var z = 1 + (yield(y));
    return z;
}
var gen = myGenerator(2);

console.log( gen.next() );

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

1

2

3

4

7

6

5

GENERATORS

  • Calling next() on Generator object starts function or resume from last yield expression
  • Params passed to next() replace yield expression
  1. myGenerator function instantiated with param x = 2
  2. next() called on generator function. No 'yield' expression encountered yet, so no param
  3. execution paused on yield expression; var y passed out as 5
  4. console logs { value: 5, done: false }
  5. execution resumes with yield expression as passed in param (6)
  6. since yield expression is 6, z becomes 7 (1 + 6)
  7. end of generator function; console logs { value: 7, done: true }

GENERATORS

  • Allows 2-way message passing into / out of functions
  • Allows for cooperative concurrency of programming

"let" and "const"

  • Alternatives to declaring a variable with 'var'
  • 'let' block-scopes variables rather than var's function- scoping

"let" and "const"

Hoisting - variables and function declarations get pulled to the top of their scope

  • Allows references to variables declared later without exceptions, but
  • Results can be unintuitive
  • Variables should be declared at top of function to avoid errors
var foo = true;

function bar() {
    if (!foo) {
        var foo = "Foo is false!"
    };
    console.log(foo)
}

bar()
> Foo is false!
var foo;

function bar() {
    var foo;
    if (!foo) {
        foo = "Foo is false!"
    };
    console.log(foo);
}

foo = true;

bar();

ES5

ES5

Console

var x = 1;
if (x) {
    var x = 2;
};
console.log(x);
> 2

{ } blocks do not create a new scope

"let" and "const"

ES5

What if we want a temporary scope for the inner x?

Console

var x = 1;
if (x) {
    (function () {
        var x = 2;
    })
};
console.log(x);
> 1

ES5

Console

Could create a new function scope

"let" and "const"

var x = 1;
if (x) {
    let x = 2;
}
console.log(x);
> 1

ES6

Console

  • Using 'let' allows for block-scoping ( {} )
  • Maintains encapsulation principles
  • Allows same variable names where appropriate
const stuff = { things: ['chair', 'desk', 'computer'] };

const stuff = { things: ['chair', 'desk', 'computer'] };
stuff = {};                                                // fails
console.log(stuff.things);                                 // ...

const stuff = { things: ['chair', 'desk', 'computer'] };
stuff.things.push('pot plant');
console.log(stuff.things);
// chair,desk,computer,pot plant

"let" and "const"

Const

  • also block-scoped
  • must be decalred using an initializer
  • can only be assigned to once 
  • will fail silently if assigned again
  • does not make the assigned value itself immutable

Arrow FUNCTIONS

  • Also known as lambda expressions
  • Makes code more 'terse'
var arrow = function() {}
var arrow = () => {}

becomes

function() {}
() => {}

becomes

  • 'return' keyword implied for single expressions in function body
  • No parentheses required for 1 argument
function(a) { return a * a }
a => a * a

becomes

  • Parentheses for 0, or >1 arguments
function(a, b) { return a + b }
(a, b) => a + b

becomes

Arrow FUNCTIONS

  • A 'fat arrow' takes the place of the 'function' keyword
function Counter() {
    var that = this;
    that.seconds = 0;
    setInterval(function() {
        that.seconds++
    }, 1000);
}
function Counter() {
    this.seconds = 0;
    setInterval(function() {
        this.seconds++
    }.bind(this), 1000);
}

Arrow FUNCTIONS

In ES5, functions create their own scope

  • Calling 'this' within the function refers to itself; not the scope outside the function
  • To bring outside scope into the function, store 'this' in a variable, or use .bind()

ES5

function Counter() {
    this.seconds = 0;
    setInterval(function() {
        this.seconds++
    }.bind(this), 1000);
}
function Counter() {
    this.seconds = 0;
    setInterval(() => this.seconds++, 1000);
}

Arrow FUNCTIONS

  • Arrow functions are bound to their lexical scope
  • 'this' in an arrow function is the same as the outer scope
  • Best used for functions with low amount of arguments/statements
  • Can't name arrow functions

ES5

ES6

Previously only one collection type in JavaScript (Array)

COLLECTION TYPES

  • Arrays only use numeric indices
    • Objects used when non-numeric indices necessary
  • Map
    • A key-value data structure new in ES6
    • Exists in many other languages

ES5 maps often created using regular Objects

var hashMap = {};

function add(key, value) {
    hashMap[key] = value;
}

function get(key) {
    return hashMap[key];
}

add('request', { description: 'Simplified HTTP request client' });
add('moment', { description: 'Parse, validate, manipulate, and display dates' });
add('lodash', { description: 'The modern build of lodash modular utilities' });

COLLECTION TYPES

var registry = {};

function add(element, meta) {
    registry[element] = meta;
}

function get(element) {
    return registry[element];
}

add(document.getElementById("wages-panel"), { display: true });
add(document.getElementById("child-income-panel"), { display: false });
add(document.getElementById("benefits-panel"), { display: true });
add(document.getElementById("otherincome-panel"), { display: true });

COLLECTION TYPES

Problems with this approach:

  • Only allows strings as keys
  • Might want to use non-string values as keys
  • Here the <div> element will be cast to string [Object HTMLDivElement]

Maps allow for any type to be used as key or value

var map = new Map();

map.set(new Date(), function today() {});
map.set(() => 'key', { foo: 'bar' });
map.set(Symbol('items'), [1, 2]);

COLLECTION TYPES

ES5

for (let key in object) {

    if (object.hasOwnProperty(key)) {
        console.log(key, object[key]);
    }
}
for (let key in object) {

    if (object.hasOwnProperty(key) &&
        typeof object[key] !== "function") {
            console.log(key, object[key]);
        }
}

COLLECTION TYPES

  • Maps have many useful iteration features
  • To iterate over a javascript object used as a map:

ES5

  • Have to avoid properties from the object prototype being included
  • Have to avoid methods from the prototype object being included

ES5

for (let key of map.keys()) {

    console.log(key);

}

for (let value of map.values()) {

    console.log(value);

}

for (let entry of map) {

    console.log(entry[0], entry[1]);

}

COLLECTION TYPES

  • With Maps, no unwanted properties /methods 'leaking' from prototype to deal with
  • Maps can iterate on keys, values or entries
  • 'entries()' default iterator for Maps
  • each key-value pair stored as two item array

ES6

COLLECTION TYPES

WeakMap

  • A subset of Map, with some limitations
    • Not iterable - no .entries(), .keys(), .values() or clear()
    • Can use .has(), .get(), .set(), .delete()
    • Every key must be an object - value types not admitted
  • A WeakMap holds references to its keys weakly
    • i.e. if no other references to a key, the entry will be garbage collected
  • Why use a WeakMap?
    • Very specific use cases
    • Mapping values to objects that might disappear in future
const privateData = new Map();

class myClass {
    constructor (name, age) {
        privateData(this, { name: name, age: age });
    }

    getname () {
        return privateData.get(this).name;
    }

    getAge() {
        return privateData.get(this).age;
    }

}

export default MyClass;

COLLECTION TYPES

  • No concept of private class variables in ES6
  • Use case - Private class variables
  • Still a reference to Map when myClass instance destroyed
  • When myClass destroyed no references to key (this) exists
  • privateData will be garbage collected and memory freed
let answersMap = new WeakMap();

let childBenefit = document.getElementById('child-benefit');

myElement.addEventListener('change', function() {
    var answerFrequency = getFrequency();
    answersMap.set(childBenefit, {frequency: answerFrequency}
}, false);

let childNumber = document.getElementById('child-number')

if(childNumber.number === 0) {
    childBenefit.parentNode.removeChild(childBenefit)
}

console.log(answerMap.get(childBenefit) //  undefined

COLLECTION TYPES

Storing data on a DOM element

  • Storing the frequency of an answer on the DOM element
  • If childBenefit key removed, entry is garbage collected

COLLECTION TYPES

  • Set
    • Very similar to map, but only have values
    • Values used as keys, so must be unique
    • no set.get() (get(value) => value)
    • set.set becomes set.add
    • use set when all values must be unique
  • WeakSet
    • As Map -> WeakMap
    • Not iterable
    • No other references to value - garbage collection

set

var text = `
    This is a template literal
    'It can span multiple lines'
    "And can contain quotation marks"
    `
var text = (
    'This is the old method\n' +
    'for strings spanning\n' +
    'several lines'
)

var text = [
    'Or',
    'perhaps',
    'this'
].join('\n')
    

TEMPLATE LITERALS

  • A string wrapped in backticks ( ` )
  • Can be multiline
  • Strings can contain ' and " characters without escaping

ES5

ES6

var text = `the time and date is ${new Date().toLocaleString()}`

var a = 6;
console.log(`The number is ${a}`)

TEMPLATE LITERALS

Can interpolate expressions with ${expression}

var foo = { bar: 'bar' }

console.log(foo.bar)    // 'bar'
var foo = { bar }

console.log(foo.bar)    // 'bar'

OBJECT LITERALS

If property value matches property name, can omit value

ES5

ES6

var foo = 'bar'
var baz = { [foo]: 'qux' }
console.log(baz)

// { bar: 'qux' }
var foo = { [bar + baz]: 'qux' }
console.log(foo)

// { barbaz: 'qux' }
function getAnswerModel (dataItem, amt, frequency) {
    return {
        ['ans_' + dataItem] : {
            amount: amt;
            frequency: freq
        }
    }
}

OBJECT LITERALS

Can compute property names using []

Possible use case:

var foo = {

    bar(baz) { }

}
var foo = {

    bar: function(baz) { }

}

OBJECT LITERALS

Declaring methods on an object

ES5

ES6

var clientDetails = {

    userName: 'Joe Bloggs',

    get name() {
        return this.userName
    },

    set name(value) {
        if (!value) {
            throw new Error('Name invalid')
        }
        this.userName= value
    }

}
    

OBJECT LITERALS

Getters and setters declared on functions

let myObject = { foo:'bar', baz: 'qux' }
let { foo, baz } = myObject

console.log(foo)                        // 'bar'

console.log(baz)                        // 'qux'

ASSIGNMENT DESTRUCTURING

An object literal lets us create multiple properties at the same time

An object pattern lets us extract multiple properties at the same time

Properties mapped to aliases

let myObject = { foo:'bar', baz: 'qux' }

let { norf: a } = myObject

console.log(a)                        // undefined

Properties can be mapped to aliases

let { foo : f, baz : b } = myObject

console.log(f)                        // 'bar'

console.log(b)                        // 'qux'

ASSIGNMENT DESTRUCTURING

Properties not found are saved as undefined

let myObject = { foo: [{ bar: 'baz', qux: 'norf' }] }

let { foo: [{ bar: a }] } = myObject

console.log(a)                        // 'baz'
let myObject = { foo:'bar', baz: undefined }

let { baz = 'default' } = myObject

console.log(baz)                        // 'default'

ASSIGNMENT DESTRUCTURING

Can define default values if 'pulled' property is undefined

Patterns can be nested arbitrarily deeply

let [, x, y] = [1, 2, 3]

console.log(x)                        // 2
console.log(y)                        // 3
let [x, y] = [1, 2, 3]

console.log(x)                        // 1
console.log(y)                        // 2

ASSIGNMENT DESTRUCTURING

Similarly, for arrays

Can skip over items not cared about

function swapIf () {
    var left = 10;
    var right = 20;
    var aux;
    
    if(right > left) {
        aux = right;
        right = left;
        left = aux;
    }
}
function swapIf () {

    var left = 10
    var right = 20    
    
    if(right > left) {
        [left, right] = [right, left]
    }
}

ASSIGNMENT DESTRUCTURING

Can swap variables without need for a temporary variable

ES5

ES6

function displayNameAge ( {name, age} ) {
    console.log(`${name} is ${age} years old`)
}

displayNameAge( { age: 27, name: 'John'} ) // 'John is 27 years old'

ASSIGNMENT DESTRUCTURING

Can destructure a function's parameter list

function getCoordinates() {
    return {
        x: 5,
        y: 22
    }
}

var {x, y} = getCoords()

console.log(x) // 5
console.log(y) // 22
function getCoordinates() {
    return {
        x: 5,
        y: 22
    }
}

var object = getCoordinates();
var x = object.x;
var y = object.y;

console.log(x) // 5
console.log(y) // 22

ASSIGNMENT DESTRUCTURING

Possible use cases

More terse interaction with returned objects

ES5

ES6

function random() ( {min=1, max=100} ) {

    return Math.floor(Math.random() * (max - min) + min

}

console.log(random( {} )) // 67
console.log(random( { max: 20 } ) // 11

ASSIGNMENT DESTRUCTURING

Possible use cases

Define default options for a method

SPREAD/REST PARAMETERS

Rest parameters - create an array out of a function's arguments

  • ES5 - used 'arguments' variable to work with large numbers of arguments
function concat() {
    return Array.prototype.slide.call(arguments).join(' ')
}

var result = concat('Lorem', 'ipsum', 'dolor', 'sit', 'amet')
console.log(result)  // 'Lorem ipsum dolor sit amet'
  • Using rest parameter syntax
function concat(...words) { 
    return words.join(' ')
}

var result = concat('Lorem', 'ipsum', 'dolor', 'sit', 'amet')
console.log(result) // 'Lorem ipsum dolor sit amet'

ES5

ES6

function concat(prefix, suffix, ...words) {
    
    return prefix + words.join(' ') + suffix

}

var result = concat('START', 'END', 'Lorem', 'ipsum', 'dolor', 'sit', 'amet')

console.log(result) // 'START Lorem ipsum dolor sit amet END'
function concat() {
    
    var words = Array.prototype.slide.call(arguments);
    var prefix = words.shift();
    var suffix = words.shift();
    return prefix + words.join(' ') + suffix;

}

var result = concat('START', 'END', 'Lorem', 'ipsum', 'dolor', 'sit', 'amet')

console.log(result)                                                  // 'START Lorem ipsum dolor sit amet END'

SPREAD/REST PARAMETERS

If 'arguments' was used, need to cut first two params by shifting

If other parameters, rest param must be rightmost

ES5

ES6

var missing = [4, 5, 6]

var sequence = [1, 2, 3, ...missing, 7, ,8 , 9]

SPREAD/REST PARAMETERS

Spread operator

  • Passes an array as arguments to a function
  • Prefix array with '...'

To recap:

  • spread/rest operators both use '...' syntax
  • Rest operator extracts data (creates array from parameters)
  • Spread operator constructs data (inserts parameters from array)
var promise = new Promise(function(resolve, reject) {
    
    // do something

    if( /* thing was done */ ) {
        resolve("Success");
    }
    else {
        reject(Error("Failed"));
    }

});
promise.then(function(result) {
    // Action on success
}, function(err) {
    // Action on error
});

Promise

Creating a promise:

Using a promise:

Takes one argument:

  • callback with 'resolve' and 'reject' params
  • Do something with the callback (perhaps async)
  • Call resolve() if worked
  • Call reject() if not

then() takes two arguments:

  • callback for success case
  • callback for failure case

Useful links

http://habrahabr.ru/post/175371/ Обзор ECMAScript 6, следующей версии JavaScript

http://habrahabr.ru/post/241275/ Ecmascript 6 — что можно использовать уже сейчас

http://es6-features.org/ New Features: Overview & Comparison

http://codecondo.com/learn-ecmascript-6-resources-javascript-devs/ 
18 Resources on ES6 for JavaScript Developers

THANKS FOR YOUR ATTENTION

ES2015

Made with Slides.com