Intro to ES6

Mike North, Principal Engineer, Yahoo Inc.

@MichaelLNorth

What is ECMAScript?

The  JavaScript standard (ECMA-262)

 

Common Implementations

 

JavaScript    JScript   ActionScript

 

TC39 - Task group

How did we get to ES6?

1997     ES1

1998     ES2 - Alignment with other standards

1999     ES3 - Regex, String utils, try/catch

                 ES4 - ABANDONED


2009     ES5 - Strict mode, Object , JSON , and Array utils

2011     ES5.1  - Alignment with new standards

2015     ES6


ES6

ES6

Scoping

  • Block scoping

  • Destructuring

  • Default param values

  • Rest parameters

  • Spread

Modularity

  • Modules

  • Standard Modules

  • Multiple Globals

  • Object Literals

  • Classes

  • Symbols

Control

  • Tail calls

  • Iterators

  • For-of loops

  • Generators

  • Array comprehensions

  • Arrow functions

Collections

  • Sets

  • Maps

  • Weak maps

Virtualization

  • Proxies

  • Loaders

Data

  • Binary data

  • Template strings

  • Octal/Binary literals

  • Unicode syntax 

Also... Array, Object, Number, Regex, String, Math, Function improvements

Native Support

Polyfills

  • 6to5 (merged with esnext)
  • Traceur
  • es6-transpiler
  • es6now
  • jstransform

Scoping

  • Block scoping
  • Destructuring
  • Default parameter values
  • Rest parameters
  • Spread

Block Scoping: Let

let

A new keyword: 

Conceptually similar to              but defines a variable only within the current

var

Works in YAM+

Block Scoping: Let

function log(msg) { console.log("LOG MESSAGE: " + msg); }

function f(x) {
    if (x > 0) {
        let { log, sin, cos } = Math;
        // log is now Math.log
        console.log('log(' + x + ')= ' +  log(5));
    }
    // log is now back to our logging function
    log("done computing f()");
}
f(3);
log('hello world!');
log(3)= 1.6094379124341003
done computing f()
hello world!

ES6

Block Scoping: Let

function log(msg) {
    console.log("LOG MESSAGE: " + msg);
}

function f(x) {
    if (x > 0) {
        let { log, sin, cos } = Math;
        // log is now Math.log
        console.log('log(' + x + ')= '
            +  log(5));
    }
    // log is now back to our logging function
    log("done computing f()");
}
f(3);
log('hello world!');
"use strict";

function log(msg) {
    console.log("LOG MESSAGE: " + msg);
}

function f(x) {
    if (x > 0) {
        var _log = Math.log;
        var sin = Math.sin;
        var cos = Math.cos;
        // log is now Math.log
        console.log("log(" + x + ")= "
            + _log(5));
    }
    // log is now back to
    //  our logging function
    log("done computing f()");
}
f(3);
log("hello world!");

ES6 -> ES5

ES6

ES5

Block Scoping: Const

const

A new keyword: 

Conceptually similar to              but defines a read-only variable. Writing to this variable throws a transpile error

let

Works in YAM+

Block Scoping: Const

const y = {};

y['a'] = 5;
console.log(y)
y = {}; //transpile error


const f = function () {console.log('hi');}

f(); //"hi"
f = function () {console.log('abc');} //transpile error

ES6

Block Scoping: Const

ES6 -> ES5




const y = {};

y['a'] = 5;
// transpile error
// y = {}; 


const f = function () {console.log('hi');}

f(); // "hi"
// transpile error
// f = function () {console.log('abc')l}

ES6

"use strict";


var y = {};

y.a = 5;
// transpile error
// y = {};


var f = function () {
  console.log("hi");
};

f(); // "hi"
// transpile error
// f = function () {console.log('abc')l}

ES5

Block Scoping: Const

  • A pure transpile-time modification
  • May be expensive as the codebase becomes large

Destructuring

Pattern matching for objects and arrays, in assignment and function parameters

Works in YAM+

Destructuring: Assignment

// Example 1, returning an object
function stats(arr) {
  var sum = arr.reduce(
    function(c,x) {
      return (c || 0) + x;
    }
  );
  return {
    sum: sum,
    mean: arr.length === 0
        ? 0
        : (sum / arr.length)
  };
}

var {sum, mean} = stats([1, 2, 3]);

console.log('Sum=', sum,
    '  Mean=', mean);

ES6

"use strict";

// Example 1, returning an object
function stats(arr) {
  var sum = arr.reduce(function (c, x) {
    return (c || 0) + x;
  });
  return {
    sum: sum,
    mean: arr.length === 0
        ? 0
        : sum / arr.length
  };
}

var _stats = stats([1, 2, 3]);

var sum = _stats.sum;
var mean = _stats.mean;

console.log("Sum=", sum,
    "  Mean=", mean);

ES5

Destructuring: Assignment



var [b,c,a] = [1,,2];
console.log("a=", a); //2
console.log("c=", c); //undefined

ES6

"use strict";

var _ref = [1,, 2];

var b = _ref[0];
var c = _ref[1];
var a = _ref[2];
console.log("a=", a); //2
console.log("c=", c); //undefined

ES5

Destructuring: Arguments




function divide({
    numerator: n,
    denominator: d }) {
  return n/d;
}

console.log('divide 6/4',
  divide({numerator: 6, denominator: 4})
);

ES6

"use strict";

function divide(_ref) {
  var n = _ref.numerator;
  var d = _ref.denominator;
  return n / d;
}
console.log("divide 6/4",
    divide({ numerator: 6, denominator: 4})
);

ES5

Default Parameter Values

Allows specification of a default value for function parameters.

Works in YAM+

Default Parameter Values

// Simple values
function addThree(a, b=4) {
  return a + b + 3;
}
addThree(4);
addThree(4,5);

// Works with arrays and objects too!
function normalizePayload(
  payload={users: []}, type) {
  console.log(payload);
};

ES6

"use strict";

// Simple values
function addThree(a) {
  var b = arguments[1] === undefined
    ? 4
    : arguments[1];
  return a + b + 3;
}
addThree(4);
addThree(4, 5);

// Works with arrays and objects too!
function normalizePayload(_x, type) {
  var payload = arguments[0] === undefined
    ? { users: [] }
    : arguments[0];
  console.log(payload);
};

ES5

Rest Parameters

  • Kind of like the "arguments" object, except altering values has no weird effects
  • It's a real array

Works in YAM+

Rest Parameters

var t = "Hello %s!"

function greet(template, ...args) {
  return args.map(function(arg) {
    return t.replace('%s', arg);
  }).join(', ');
}

console.log(greet(t, 'Mike', 'Dave', 'Steve'));
//  Hello Mike!, Hello Dave!, Hello Steve!

ES6

"use strict";

var t = "Hello %s!";

function greet(template) {
  for (var _len = arguments.length,
            args = Array(_len > 1
                ? _len - 1
                : 0),
            _key = 1;
        _key < _len;
        _key++) {
    args[_key - 1] = arguments[_key];
  }

  return args.map(function (arg) {
    return t.replace("%s", arg);
  }).join(", ");
}

console.log(greet(t, "Mike", "Dave", "Steve"));
//  Hello Mike!, Hello Dave!, Hello Steve!

ES5

Rest Parameters

var t = "Hello %s!"

function greet(template, ...args) {
  return args.map(function(arg) {
    return t.replace('%s', arg);
  }).join(', ');
}

console.log(greet(t, 'Mike', 'Dave', 'Steve'));
//  Hello Mike!, Hello Dave!, Hello Steve!

ES6

"use strict";

var t = "Hello %s!";

function greet(template) {
  for (var _len = arguments.length,
            args = Array(_len > 1
                ? _len - 1
                : 0),
            _key = 1;
        _key < _len;
        _key++) {
    args[_key - 1] = arguments[_key];
  }

  return args.map(function (arg) {
    return t.replace("%s", arg);
  }).join(", ");
}

console.log(greet(t, "Mike", "Dave", "Steve"));
//  Hello Mike!, Hello Dave!, Hello Steve!

ES5

Spread

  • Expands arrays to act like multiple  variables
  • A better Function.prototype.apply
  • Easy array concatenation

Works in YAM+

Spread

var b = [2, 3];
var a = [1, ...b, 5];
console.log(a); // [1, 2, 3, 5]
a.push(...b);
console.log(a); // [1, 2, 3, 5, 2, 3]

ES6

"use strict";

var _toArray = function (arr) {
    return Array.isArray(arr)
        ? arr
        : Array.from(arr);
};

var b = [2, 3];
var a = [1].concat(_toArray(b), [5]);
console.log(a); // [1, 2, 3, 5]
a.push.apply(a, _toArray(b));
console.log(a); // [1, 2, 3, 5, 2, 3]

ES5

Spread



var b = [2, 3];
var a = [1, ...b, 5];
console.log(a); // [1, 2, 3, 5]
a.push(...b);
console.log(a); // [1, 2, 3, 5, 2, 3]

ES6

"use strict";

var _toArray = function (arr) {
    return Array.isArray(arr)
        ? arr
        : Array.from(arr);
};

function f(a, b, c) {
  console.log("a=", a, "b=", b, "c=", c);
}

function a() {
  var args = [1, 2, 3];
  f.apply(undefined, _toArray(args));
}

a(); // a= 1 b= 2 c= 3

ES5

Modularity

  • Modules
  • Object Literals
  • Classes
  • Symbols

Modules

  • Allows for better encapsulation of code on a per-file bases
  • Built on patterns from YUI, AMD, CommonJS, etc...
  • No code executes until requested modules are available and processed (async friendly)

Works in YAM+

Modules: Exports




export function sum(x, y) {
  return x + y;
}
export var pi = 3.141593;

ES6

"use strict";

// lib/math.js
exports.sum = sum;
function sum(x, y) {
  return x + y;
}
var pi = exports.pi = 3.141593;
exports.__esModule = true;

ES5

Modules: Imports


// app.js
import * as math from "lib/math";
alert("2π = " + math.sum(math.pi, math.pi));

ES6

"use strict";

var _interopRequireWildcard = 
    function (obj) {
        return obj && obj.__esModule
            ? obj
            : { "default": obj };
    };

// app.js
var math = _interopRequireWildcard(
    require("lib/math")
);

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

ES5

Object Literals

  • Prototype and methods can be set at part of construction
  • Super calls!
  • Dynamic property names

Works in YAM+

Object Literals

var firstName = 'Mike',
  lastName = 'North';

var x = {
  firstName,
  lastName,
  ['age']: 31,
  toString() {
    return JSON.stringify(this);
  }
}

console.log(x);

ES6

"use strict";

var firstName = "Mike",
    lastName = "North";

var x = (function () {
  var _x = {};

  _x.firstName = firstName;
  _x.lastName = lastName;
  _x.age = 31;
  _x.toString = function toString() {
    return JSON.stringify(this);
  };

  return _x;
})();

console.log(x);

ES5

Classes

  • Syntactic sugar over prototypical inheritance
  • Presents some of the same advantages as classical inheritance, in declarative form
    • super calls
    • instance methods
    • static methods
    • constructors

Works in YAM+

Classes

class Animal extends Object {

  constructor (name='Animal') {
    this.name = name
  }
  
  eat() {
    console.log(this.name + " is eating");
  }
}

class Canine extends Animal {
  static numLegs() {return 4;}

  constructor (name='Canine') {
    super(name);
  }
  eat() {
    super();
    console.log('...and then chasing his tail');
  }
}

class Dog extends Canine {
  constructor (name='Dog') {
    super(name);
  }
}

var d = new Dog();
d.eat(); // Dog is eating ...and then chasing his tail
var dd = new Dog('Charlie');
dd.eat(); // Charlie is eating ...and then chasing his tail
console.log(Dog.numLegs()); // 4

ES6

class Dog extends Canine {
  constructor (name='Dog') {
    super(name);
  }
}

var d = new Dog();
d.eat();
// Dog is eating ...and then chasing his tail

var dd = new Dog('Charlie');
dd.eat();
// Charlie is eating ...and then chasing his tail

console.log(Dog.numLegs());
// 4

ES6

Classes

  • Remember, this is still prototypical inheritance
  • All member data is set up in constructor

Symbols

  • A new type of object
  • Immutable
  • Unique within a module
  • Allows better hiding of private data

Works in YAM+

Needs polyfill

Symbols

// Create an object
var o = {};

// Create a symbol, with an optional name
var key = Symbol('a');

// Set a property of "o" using key
o[key] = 'testing';

// Create another symbol with an optional name
var other_key = Symbol('a');
console.log(other_key.toString()) // Symbol(testing)

// Retrieving the value we set above for key can't be accessed
console.log(o[other_key]); // undefined

// Properties under keys are by default not enumerable
console.log(o); // {}

ES6

Control

  • Arrow Functions
  • Array comprehensions
  • Iterators
  • For-of Loop
  • Generators
  • Generator Comprehensions

Arrow Functions

  • Similar to C#, Java 8 concepts
  • Behaves like a function, except "this" is always same as parent closure

Works in YAM+

Arrow Functions

var obj = {
  
  items: ['apple', 'pear'],
  
  formatItem: function(item) {
    return item.toUpperCase();
  },
  
  print: function () {
    console.log(
      this.items.map(
        x => this.formatItem(x)
      )
    );
  }
};

obj.print();

ES6

"use strict";

var obj = {

  items: ["apple", "pear"],

  formatItem: function (item) {
    return item.toUpperCase();
  },

  print: function () {
    var _this = this;
    console.log(this.items.map(
        function (x) {
          return _this.formatItem(x);
        }
    ));
  }
};

obj.print();

ES5

Comprehensions

  • New ways of defining arrays using expressions instead of literal assignment

Works in YAM+

Needs polyfill

Comprehensions



var customers = [
  {city: 'New York', name: 'Kay', age: 31},
  {city: 'Seattle', name: 'Jo', age: 36}
];

var results = [
  for(c of customers)
    if (c.city == "Seattle")
      { name: c.name, age: c.age }
]


console.log('results=', results);
// results= [{"name":"Jim","age":36}]

ES6

Iterators

  • Allows custom iteration
  • Similar to a Java's java.lang.Iterable<T>
  • Can be of a finite or infinite length
  • Calculate as you go

Works in YAM+

Needs polyfill

Iterators

let fibonacci = {
  [Symbol.iterator]() {
    let pre = 0, cur = 1;
    return {
      next() {
        [pre, cur] = [cur, pre + cur];
        return {
          done: cur > 100,
          value: cur
        };
      }
    }
  }
}

var arr = []
for (var x of fibonacci) {
  arr.push(x);
}

console.log(arr.join(', '));
 // 1, 2, 3, 5, 8, 13, 21, 34, 55, 89

ES6

Generators

  • Simplify building iterators  using
    • function*
    • yield
  • Important concepts: next, throw

Works in YAM+

Needs polyfill

Generators

let fibonacci = {
  [Symbol.iterator]() {
    let pre = 0, cur = 1;
    return {
      next() {
        [pre, cur] = [cur, pre + cur];
        return {
          done: cur > 100,
          value: cur
        };
      }
    }
  }
}

var arr = []
for (var x of fibonacci) {
  arr.push(x);
}

console.log(arr.join(', '));
 // 1, 2, 3, 5, 8, 13, 21, 34, 55, 89

ES6

Collections

  • Map
  • Set
  • Weak Map
  • Weak Set

Map

  • A simple key/value data structure
  • Any value may be used as either a key or value
  • Size can be retrieved easily

Works in YAM+

Needs polyfill

Map

var m = new Map([['a', 'b'], ['c', 'd']]);

m.size; // 2

m.forEach(function(key, value, map) {
  ...
});


m.get('a'); // 'b'
m.has('a'); // true
m.set('a', 14); 

m.entries(); // MapIterator for key, value pairs
m.keys(); // MapIterator for keys
m.values(); // MapIterator for values

ES6

Set

  • Stores a collection of unique values
  • Can handle primitive values or object references equally well
  • Uses ===

Works in YAM+

Needs polyfill

Set

var d = 'a';

var s = new Set(['a', 'b', 'c', d]);

s.size; // 3

s.has('A'.toLowerCase()); // true

ES6

WeakMap

  • Keys must be Objects
  • Keys may be garbage collected

Works in YAM+

Needs polyfill

WeakMap

var records = [
  {id: 1, firstName: 'Walter', lastName: 'White'},
  {id: 2, firstName: 'Jesse', lastName: 'Pinkman'}
];

var recordMetaData = new WeakMap(
  records.map(
    (r) => (
      [r, {fullName: `${r.firstName} ${r.lastName}`}]
    )
  )
);
  
console.log(recordMetaData.get(records[0]).fullName);
// Walter White

console.log(recordMetaData.get(records[1]).fullName);
// Jesse Pinkman

ES6

WeakSet

  • Just like Set, except that
    • It holds weak references to members, so they can be garbage collected
    • It's not iterable
    • Keys must be Objects

Works in YAM+

Needs polyfill

Tips for ES6 Use

  • Consider computational cost
  • Remember our coding values
    • Testability
    • Maintainability
    • Clarity
  • Learn as much as you can!

Resources

Intro to ES6

By Mike North