ES6 (ES2015)

Features

by Martin Micunda

History of JavaScript

1995

ES3

1999

ES5

2009

ES6

2015

ES4

2007

History of JavaScript

1995

JavaScript is introduced by Brendan Eich as part of Netscape.

ES3

1999

ES5

2009

ES6

2015

ES4

2007

History of JavaScript

JavaScript is introduced by Brendan Eich as part of Netscape.

- try/catch

- RegExp 

- String functions

- much of the core language used today

 

1995

ES3

1999

ES5

2009

ES6

2015

ES4

2007

History of JavaScript

JavaScript is introduced by Brendan Eich as part of Netscape.

- try/catch

- RegExp 

- String functions

- much of the core language used today

 

- Types

- Classes 

- Modules

- (other)

 

1995

ES3

1999

ES5

2009

ES6

2015

ES4

2007

History of JavaScript

JavaScript is introduced by Brendan Eich as part of Netscape.

- try/catch

- RegExp 

- String functions

- much of the core language used today

 

- Types

- Classes 

- Modules

- (other)

 

- getter/setters

- JSON parsing 

- strict mode

 

1995

ES3

1999

ES5

2009

ES6

2015

ES4

2007

History of JavaScript

JavaScript is introduced by Brendan Eich as part of Netscape.

- try/catch

- RegExp 

- String functions

- much of the core language used today

- Types

- Classes 

- Modules

- (other)

 

- getter/setters

- JSON parsing 

- strict mode

 

- Classes 

- Modules

- Object APIs

- Collections​- Rest/Spread

- (other)

 

1995

ES3

1999

ES5

2009

ES6

2015

ES4

2007

Goals for ES6

Goals for ES6 is to be a better language for writing:

  • complex applications
  • libraries shared by those applications
  • code generators targeting the new edition

ES6 Features

Block Scope

var x = 10; // function or global scope (good old var)
let y = 10; // block scoped 
const MY_NUMBER = 10; // can't change it, block scoped
  • ES6 introduces a third scope, block scope
  • No more hoisting
function test() {
  var x = 10;
  if (true) {
    let x = 15;
    let y = 20;
    console.log(x); // 15
    console.log(y); // 20
  }
  console.log(x); // 10
  console.log(y); // ReferenceError y is not defined
}

var vs let

function test() {
  var x = 10;
  if (true) {
    let x = 15;
    let y = 20;
    console.log(x); // 15
    console.log(y); // 20
  }
  console.log(x); // 10
  console.log(y); // ReferenceError y is not defined
}
if (true) {
  console.log(value); // ReferenceError! 
  let value = "blue";
}

var vs let

var funcs = [];

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

funcs[3](); // 5

var vs let

var funcs = [];

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

funcs[3](); // 3
var funcs = [];

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

funcs[3](); // 5

var vs let

var funcs = [];

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

funcs[3](); // 3

Tipuse let instead of var

{
  var MAX_ITEMS = 2;

  console.log( a ); // 2

  a = 3; // This is valid!
}

const

The old way

{
  var MAX_ITEMS = 2;

  console.log( a ); // 2

  a = 3; // This is valid!
}

const

The old way

The new way

{
  const MAX_ITEMS = 2;

  console.log( a ); // 2

  a = 3; // TypeError!
}

Default Parameters

function foo(x,y) { 
  x = x || 11; 
  y = y || 31;
  
  console.log( x + y );  
}

foo(); // 42
foo( 5, 6 ); // 11
foo( 5 );  // 36
foo( null, 6 ); // 17
foo( 0, 42 ); // 53 <-- Oops, not 42

The old way

function foo(x,y) { 
  x = x || 11; 
  y = y || 31;
  
  console.log( x + y );  
}

foo(); // 42
foo( 5, 6 ); // 11
foo( 5 );  // 36
foo( null, 6 ); // 17
foo( 0, 42 ); // 53 <-- Oops, not 42

The old way

The new way

function foo(x = 11, y = 31) { 
  console.log( x + y );  
}

foo(); // 42
foo( 5, 6 ); // 11
foo( 5 );  // 36
foo( null, 6 ); // 17
foo( 0, 42 ); // 42
// only the first parameter is expected to be passed all the time
function makeRequest(url, timeout = 2000, callback = function() {}) { 
  ...
}

// uses default timeout and callback
makeRequest("/foo");

// uses default callback
makeRequest("/foo", 500);

// doesn't use defaults
makeRequest("/foo", 500, function(body) { 
  doSomething(body);
});

Rest Parameters

function logMyBooks(){
  Array.isArray(arguments); // false
  var books = Array.prototype.slice.call(arguments);
  books.forEach(function(book){
    console.log(book);
  })
}

logMyBooks('Book 1','Book 2');

The old way

function logMyBooks(){
  Array.isArray(arguments); // false
  var books = Array.prototype.slice.call(arguments);
  books.forEach(function(book){
    console.log(book);
  })
}

logMyBooks('Book 1','Book 2');

The old way

The new way

function logMyBooks(...books){
  Array.isArray(books); // true
  books.forEach(function(book){
    console.log(book);
  })
}

logMyBooks('Book 1','Book 2');
function logMyBooks(){
  Array.isArray(arguments); // false
  var books = Array.prototype.slice.call(arguments);
  books.forEach(function(book){
    console.log(book);
  })
}

logMyBooks('Book 1','Book 2');

The old way

The new way

function logMyBooks(...books){
  Array.isArray(books); // true
  books.forEach(function(book){
    console.log(book);
  })
}

logMyBooks('Book 1','Book 2');
function logMyBooks(author, ...books){
  console.log(author, books); // 'Martin' [Book 1','Book 2']
}

logMyBooks('Martin','Book 1','Book 2');

Spread Parameters

  • The inverse of rest parameters
  • Mostly replaces Function.prototype.apply()
function logMyBooks(book1, book2){
  console.log(book1, book2);
}
logMyBooks.apply(null, ['Book 1','Book 2']);

let values = [25, 50, 75, 100] 
console.log(Math.max.apply(Math, values)); // 100

The old way

function logMyBooks(book1, book2){
  console.log(book1, book2);
}
logMyBooks.apply(null, ['Book 1','Book 2']);

let values = [25, 50, 75, 100] 
console.log(Math.max.apply(Math, values)); // 100

The old way

The new way

function logMyBooks(book1, book2){
  console.log(book1, book2);
}
logMyBooks(...['Book 1','Book 2']);

let values = [25, 50, 75, 100] 
console.log(Math.max(...values)); // 100
function logMyBooks(book1, book2){
  console.log(book1, book2);
}
logMyBooks.apply(null, ['Book 1','Book 2']);

let values = [25, 50, 75, 100] 
console.log(Math.max.apply(Math, values)); // 100

The old way

The new way

function logMyBooks(book1, book2){
  console.log(book1, book2);
}
logMyBooks(...['Book 1','Book 2']);

let values = [25, 50, 75, 100] 
console.log(Math.max(...values)); // 100

Concatenate arrays

let a = [1, ...[2,3], 4]; // [1, 2, 3, 4]

let x = ['a', 'b'];
let y = ['c'];
let z = ['d', 'e'];
let xyz = [...x, ...y, ...z]; // ['a', 'b', 'c', 'd', 'e']

Destructuring

Object Pattern

function today() { 
  return {
    month: 6, 
    day: 2, 
    year: 2015
  }; 
}
let month = today().month, day = today().day;
console.log(month, day); // 6 2

The old way

Object Pattern

function today() { 
  return {
    month: 6, 
    day: 2, 
    year: 2015
  }; 
}
let month = today().month, day = today().day;
console.log(month, day); // 6 2

The old way

The new way

function today() { 
  return {
    month: 6, 
    day: 2, 
    year: 2015
  }; 
}
let {m: month, d: day} = today();
console.log(month, day); // 6 2

Object Pattern

function today() { 
  return {
    month: 6, 
    day: 2, 
    year: 2015
  }; 
}
let month = today().month, day = today().day;
console.log(month, day); // 6 2

The old way

The new way

function today() { 
  return {
    month: 6, 
    day: 2, 
    year: 2015
  }; 
}
let {m: month, d: day} = today();
console.log(month, day); // 6 2
console.log(m, d); // ReferenceError

Object Pattern

function today() { 
  return {
    month: 6, 
    day: 2, 
    year: 2015
  }; 
}
let month = today().month, day = today().day;
console.log(month, day); // 6 2

The old way

The new way

function today() { 
  return {
    month: 6, 
    day: 2, 
    year: 2015
  }; 
}
let {m: month, d: day} = today();
console.log(month, day); // 6 2
console.log(m, d); // ReferenceError

let {month, day} = today();
console.log(month, day); // 6 2

Array Pattern

function today() { 
  return [6,2,2015];
}
let month = today()[0], day = today()[1];
console.log(month, day); // 6 2

The old way

Array Pattern

function today() { 
  return [6,2,2015];
}
let month = today()[0], day = today()[1];
console.log(month, day); // 6 2

The old way

The new way

function today() { 
  return [6,2,2015];
}
let [month, day] = today();
console.log(month, day); // 6 2

Object Literal Shorthand

The old way

function createPerson() { 
    let name="Martin", age=28;
    return {
        name: name,
        age: age 
    };
}

The old way

function createPerson() { 
    let name="Martin", age=28;
    return {
        name: name,
        age: age 
    };
}

The new way

function createPerson() { 
    let name="Martin", age=28;
    return {
        name,
        age 
    };
}

The old way

function createPerson() { 
    let name="Martin", age=28;
    return {
        name: name,
        age: age 
    };
}

The new way

function createPerson() { 
    let name="Martin", age=28;
    return {
        name,
        age 
    };
}
function createPerson() { 
    var name="Martin", age=28;
    return {
        fullName: name,
        age 
    };
}

The old way

var person = {
    name: "Martin", 
    sayName: function() {
        console.log(this.name); 
    }
};

The old way

var person = {
    name: "Martin", 
    sayName: function() {
        console.log(this.name); 
    }
};

The new way

var person = {
    name: "Martin", 
    sayName() {
        console.log(this.name); 
    }
};

Template Strings

let name = "Martin";
let greeting = "Hello " + name + "!";

The old way

let name = "Martin";
let greeting = "Hello " + name + "!";

The old way

The new way

let name = "Martin";
let greeting = `Hello ${name}!`;
let name = "Martin";
let greeting = "Hello " + name + "!";

The old way

The new way

let name = "Martin";
let greeting = `Hello ${name}!`;
function upper(s) {
    return s.toUpperCase();
}
var text = `Hello ${upper( "martin" )}!`
console.log( text ); // Hello MARTIN!

Any valid expression is allowed

  • Template strings can be multiline

The old way

  • Template strings can be multiline
let message = [ 
    "Multiline ",
    "string"
].join("");

let message = "Multiline " + 
    "string";

The old way

The new way

let message = `Multiline 
string`;

console.log(message.length); // 16

  • Template strings can be multiline
let message = [ 
    "Multiline ",
    "string"
].join("");

let message = "Multiline " + 
    "string";

The old way

The new way

let message = `Multiline 
string`;

console.log(message.length); // 16

// all whitespace inside of the backticks 
// is considered to be part of the string
let message = `Multiline 
              string`;

console.log(message.length); // 32
  • Template strings can be multiline
let message = [ 
    "Multiline ",
    "string"
].join("");

let message = "Multiline " + 
    "string";

for .. of Loops

Replaces:

  • for-in
  • Array.prototype.forEach()
// loops over the keys/indexes in the a array
var a = ["a","b","c","d","e"];
for(var idx in a){ 
  console.log( idx );
}
// 0 1 2 3 4

Replaces:

  • for-in
  • Array.prototype.forEach()
// loops over the keys/indexes in the a array
var a = ["a","b","c","d","e"];
for(var idx in a){ 
  console.log( idx );
}
// 0 1 2 3 4

Replaces:

  • for-in
  • Array.prototype.forEach()
// loops over the values in the a array
var a = ["a","b","c","d","e"];
for(var val of a){ 
  console.log( val );
}
// "a" "b" "c" "d" "e"

Arrow Functions

  • Dropping function, return, and { .. } from your code
  • Dropping function, return, and { .. } from your code
function foo(x,y) { 
  return x + y;
}

The old way

  • Dropping function, return, and { .. } from your code
function foo(x,y) { 
  return x + y;
}

The old way

The new way

var foo = (x,y) => x + y;
  • Dropping function, return, and { .. } from your code
function foo(x,y) { 
  return x + y;
}

The old way

The new way

var foo = (x,y) => x + y;

Arrow function variations

var f1 = () => 12; 
var f2 = x => x * 2; 
var f3 = (x,y) => {
  var z = x * 2 + y; 
  y++;
  x *=3; 
  return (x + y + z) / 2;
};
  • Lexical this binding - arrow functions automatically bind "this" from the containing scope
function Thing() {
  this.things = [1, 2, 3];
  this.squaredThings = this.things.map(function(x) {
    return this.square(x);
  }.bind(this));
}

Thing.prototype.square = function(n) {
  return n * n;
}

The old way

  • Lexical this binding - arrow functions automatically bind "this" from the containing scope
function Thing() {
  this.things = [1, 2, 3];
  this.squaredThings = this.things.map(function(x) {
    return this.square(x);
  }.bind(this));
}

Thing.prototype.square = function(n) {
  return n * n;
}

The old way

The new way

function Thing() {
  this.things = [1, 2, 3];
  this.squaredThings = this.things.map(x => this.square(x)); 
}

Thing.prototype.square = function(n) {
  return n * n;
}
  • Lexical this binding - arrow functions automatically bind "this" from the containing scope

Map, Set

Map

  • Just a simple Key-Value pair
  • The key can be any value, not just primitives

Map

  • Just a simple Key-Value pair
  • The key can be any value, not just primitives
let m = new Map();
m.set("hello", 42);
m.get("hello");
                

Map

  • Just a simple Key-Value pair
  • The key can be any value, not just primitives
let m = new Map();
m.set("hello", 42);
m.get("hello");
                
// create an object
const Person = {
  name: "Martin"
}
// add the complex key
m.set(Person, "present");
// get the value
console.log(m.get(Person)); // present

Map

  • Just a simple Key-Value pair
  • The key can be any value, not just primitives
let m = new Map();
m.set("hello", 42);
m.get("hello");
                
// create an object
const Person = {
  name: "Martin"
}
// add the complex key
m.set(Person, "present");
// get the value
console.log(m.get(Person)); // present

// iterable
for (let value of m.values()) {
  console.log("value: " + value);
}
for (let key of m.keys()) {
  console.log("key: " + JSON.stringify(key));
}
for (let [key,value] of m) {
  console.log(key, value);
}

Set

  • Collection of unique values (duplicates are ignored)
  • Iterable in insertion order

Set

  • Collection of unique values (duplicates are ignored)
  • Iterable in insertion order
let s = new Set();
s.add("hello");
                

Set

  • Collection of unique values (duplicates are ignored)
  • Iterable in insertion order
let s = new Set();
s.add("hello");
                
// create an object
const Person = {
    name: "Charlie"
}
// add the complex value
s.add(Person);
// get the value
s.has(Person) === true;

Set

  • Collection of unique values (duplicates are ignored)
  • Iterable in insertion order
let s = new Set();
s.add("hello");
                
// create an object
const Person = {
    name: "Charlie"
}
// add the complex value
s.add(Person);
// get the value
s.has(Person) === true;

// try re-add the same value, ignored
s.add(Person);

 

Set

  • Collection of unique values (duplicates are ignored)
  • Iterable in insertion order
let s = new Set();
s.add("hello");
                
// create an object
const Person = {
    name: "Charlie"
}
// add the complex value
s.add(Person);
// get the value
s.has(Person) === true;

// try re-add the same value, ignored
s.add(Person);

// iterable
for (var item of s) {
    console.log(item);
}

Promises

  • Have existed in libs like when.js, Q, RSVP, Bluebird & jQuery
  • The ECMAScript 6 promise API follows Promises/A+ standart
function someLongTask() {
    return new Promise(function(resolve, reject) {
        setTimeout(function() {
            resolve("someLongTask");
        }, 1000);
    });
}

function someOtherLongTask(msg) {
    return new Promise(function(resolve,reject) {
        setTimeout(function(){
             resolve("someOtherLongTask says: " + msg);
        }, 1500);
    });
}

Producing a promise

function someLongTask() {
    return new Promise(function(resolve, reject) {
        setTimeout(function() {
            resolve("someLongTask");
        }, 1000);
    });
}

function someOtherLongTask(msg) {
    return new Promise(function(resolve,reject) {
        setTimeout(function(){
             resolve("someOtherLongTask says: " + msg);
        }, 1500);
    });
}

Producing a promise

Consuming a promise

someLongTask()
.then(function (msg) {
    console.log("Single: " + msg);
})
.catch(function (reason) {
    console.error("Error: ", reason);
});
someLongTask()
.then(someOtherLongTask)
.then(function (msg) {
    console.log("Chained: " + msg);
});

Chaining a promise

someLongTask()
.then(someOtherLongTask)
.then(function (msg) {
    console.log("Chained: " + msg);
});

Chaining a promise

Promise.all

var promises = [someLongTask(), someOtherLongTask("Hello")];

Promise.all(promises).then(function(results) {
    var msg1 = results[0];
    var msg2 = results[1];
    console.log("All: " + msg1+ " then "+ msg2);
});
someLongTask()
.then(someOtherLongTask)
.then(function (msg) {
    console.log("Chained: " + msg);
});

Chaining a promise

Promise.all

var promises = [someLongTask(), someOtherLongTask("Hello")];

Promise.all(promises).then(function(results) {
    var msg1 = results[0];
    var msg2 = results[1];
    console.log("All: " + msg1+ " then "+ msg2);
});

Tip: for large application use Bluebird promise as it's faster then native promise (native promise vs bluebird promise)

Generators

Syntax

function *foo () { ... }

A generator is defined by an * character and can be paused and resumed at any time in your app

Syntax

function *foo () { ... }

A generator is defined by an * character and can be paused and resumed at any time in your app

A generator function is paused by executing a yield keyword in the body of the function

function *foo () { yield 'bar'; }

Syntax

function *foo () { ... }

A generator is defined by an * character and can be paused and resumed at any time in your app

A generator function is paused by executing a yield keyword in the body of the function

function *foo () { yield 'bar'; }

When a generator function is called, it returns a Generator Iterator object

var iterator = foo(); 

Syntax

function *foo () { ... }

A generator is defined by an * character and can be paused and resumed at any time in your app

A generator function is paused by executing a yield keyword in the body of the function

function *foo () { yield 'bar'; }

When a generator function is called, it returns a Generator Iterator object

var iterator = foo();  

The Generator Iterator includes a next() method for stepping through the generator function and next() method returns an object with ‘value’ and ‘done’ properties.

Base Example

function *foo() {  
  console.log('execution starts');
  yield 'one';
  yield 'two';
  yield 'three';
  console.log('execution completes');
}

Base Example

function *foo() {  
  console.log('execution starts');
  yield 'one';
  yield 'two';
  yield 'three';
  console.log('execution completes');
}

// create and iterator object
let myFoo = foo();






// console output

Base Example

function *foo() {  
  console.log('execution starts');
  yield 'one';
  yield 'two';
  yield 'three';
  console.log('execution completes');
}

// create and iterator object
let myFoo = foo();
// executes foo until the first yield statement and return 1
console.log(myFoo.next());  



  
// console output
execution starts
Object {value: "one", done: false}

Base Example

function *foo() {  
  console.log('execution starts');
  yield 'one';
  yield 'two';
  yield 'three';
  console.log('execution completes');
}

// create and iterator object
let myFoo = foo();
// executes foo until the first yield statement and return 1
console.log(myFoo.next());  
console.log(myFoo.next());  


  
// console output
execution starts
Object {value: "one", done: false}
Object {value: "two", done: false}

Base Example

function *foo() {  
  console.log('execution starts');
  yield 'one';
  yield 'two';
  yield 'three';
  console.log('execution completes');
}

// create and iterator object
let myFoo = foo();
// executes foo until the first yield statement and return 1
console.log(myFoo.next());  
console.log(myFoo.next());  
console.log(myFoo.next());  
  

// console output
execution starts
Object {value: "one", done: false}
Object {value: "two", done: false}
Object {value: "three", done: false}

Base Example

function *foo() {  
  console.log('execution starts');
  yield 'one';
  yield 'two';
  yield 'three';
  console.log('execution completes');
}

// create and iterator object
let myFoo = foo();
// executes foo until the first yield statement and return 1
console.log(myFoo.next());  
console.log(myFoo.next());  
console.log(myFoo.next());  
console.log(myFoo.next());  

// console output
execution starts
Object {value: "one", done: false}
Object {value: "two", done: false}
Object {value: "three", done: false}
execution completes
Object {value: undefined, done: true}

Real Example - Callbacks

var restaurant = function(complete) {  
  takeOrder(['fish', 'sandwich', 'pizza'], function(err, order) {




















  });
};

Real Example - Callbacks

var restaurant = function(complete) {  
  takeOrder(['fish', 'sandwich', 'pizza'], function(err, order) {
    cookFood(order, function(err, meals) {


















    });
  });
};

Real Example - Callbacks

var restaurant = function(complete) {  
  takeOrder(['fish', 'sandwich', 'pizza'], function(err, order) {
    cookFood(order, function(err, meals) {
      if(err) {
        console.log(err.message);
        complete(err);
      } else {
      












      }
    });
  });
};

Real Example - Callbacks

var restaurant = function(complete) {  
  takeOrder(['fish', 'sandwich', 'pizza'], function(err, order) {
    cookFood(order, function(err, meals) {
      if(err) {
        console.log(err.message);
        complete(err);
      } else {
    









        eat(meals[0],                );
        eat(meals[1],                );
        eat(meals[2],                );
      }
    });
  });
};

Real Example - Callbacks

var restaurant = function(complete) {  
  takeOrder(['fish', 'sandwich', 'pizza'], function(err, order) {
    cookFood(order, function(err, meals) {
      if(err) {
        console.log(err.message);
        complete(err);
      } else {
        var counter = 3;
        var payment = 0;
        var waitForCustomer = function(err, money) {
          counter--;
          payment += money;
          if(counter < 1) {
            complete(null, payment);
          }
        };

        eat(meals[0], waitForCustomer);
        eat(meals[1], waitForCustomer);
        eat(meals[2], waitForCustomer);
      }
    });
  });
};

Real Example - Promises

var restaurant = function() {  
  return takeOrder(['fish', 'sandwich', 'pizza'])
    .then(function(order) {
      return cookFood(order);
    })
    .then(function(meals) {
      return Promise.all([eat(meals[0]), eat(meals[1]), eat(meals[2])]);
    }, function(err) {
      console.log(err.message);
      return Promise.reject(err);
    })
    .then(function(monies) {
      var total = 0;
      monies.forEach(function(r){ total += r; });
      return total;
    });
};

Real Example - Generators

  
var restaurant = function *() {  
  var order = yield takeOrder(['fish', 'sandwich', 'pizza']);

  var meals = yield cookFood(order);
  var total = 0;
  var monies = yield Promise.all([eat(meals[0]), eat(meals[1]), eat(meals[2])]);
  monies.forEach(function(r){ total += r; });
  yield Promise.resolve(total);



};

Real Example - Generators

 
var restaurant = function *() {  
  var order = yield takeOrder(['fish', 'sandwich', 'pizza']);
  try {
    var meals = yield cookFood(order);
    var total = 0;
    var monies = yield Promise.all([eat(meals[0]), eat(meals[1]), eat(meals[2])]);
    monies.forEach(function(r){ total += r; });
    yield Promise.resolve(total);
  } catch(err) {
    console.log(err.message);
  }
};

Real Example - Generators

var async = Bluebird.coroutine;  
var restaurant = async(function *() {  
  var order = yield takeOrder(['fish', 'sandwich', 'pizza']);
  try {
    var meals = yield cookFood(order);
    var total = 0;
    var monies = yield Promise.all([eat(meals[0]), eat(meals[1]), eat(meals[2])]);
    monies.forEach(function(r){ total += r; });
    yield Promise.resolve(total);
  } catch(err) {
    console.log(err.message);
  }
});

Real Example - ES7 async and await


var restaurant = async function() {  
  var order = await takeOrder(['fish', 'sandwich', 'pizza']);
  try {
    var meals = await cookFood(order);
    var total = 0;
    var monies = await Promise.all([eat(meals[0]), eat(meals[1]), eat(meals[2])]);
    monies.forEach(function(r){ total += r; });
    return total;
  } catch(err) {
    console.log(err.message);
  }
};

Classes

  • Classes are syntactical sugar over prototypes & object instances.
function Person(firstName, lastName) { 
  this.firstName = firstName;
  this.lastName = lastName;
}

Person.prototype.displayName = function() {
  return this.firstName + " " + this.lastName; 
}

var person = new Person("Martin", "Micunda");
console.log(person.displayName()); // Martin Micunda

The old way

function Person(firstName, lastName) { 
  this.firstName = firstName;
  this.lastName = lastName;
}

Person.prototype.displayName = function() {
  return this.firstName + " " + this.lastName; 
}

var person = new Person("Martin", "Micunda");
console.log(person.displayName()); // Martin Micunda

The old way

The new way

class Person {
  constructor(firstName, lastName) {
    this.firstName = firstName;
    this.lastName = lastName;
  }
 
  displayName() {
    return `${this.firstName} ${this.lastName}`; 
  }
}

let person = new Person("Martin", "Micunda");
console.log(person.displayName()); // Martin Micunda

Classes - Inheritance

function Employee(firstName, lastName, employeeId) {
  Person.call(this, firstName, lastName); 
  this.employeeId = employeeId;
}
Employee.prototype = Object.create(Person.prototype);
Employee.prototype.constructor = Employee;
Employee.prototype.describe = function() {
  return Person.prototype.displayName.call(this) 
         + " (ID: " + this.employeeId + ")";
}

let employee = new Employee("Martin", "Micunda", 1234);
console.log(employee.describe()); // Martin Micunda (ID: 1234)

The old way

function Employee(firstName, lastName, employeeId) {
  Person.call(this, firstName, lastName); 
  this.employeeId = employeeId;
}
Employee.prototype = Object.create(Person.prototype);
Employee.prototype.constructor = Employee;
Employee.prototype.describe = function() {
  return Person.prototype.displayName.call(this) 
         + " (ID: " + this.employeeId + ")";
}

let employee = new Employee("Martin", "Micunda", 1234);
console.log(employee.describe()); // Martin Micunda (ID: 1234)

The old way

The new way

class Employee extends Person {
  constructor(firstName, lastName, employeeId) {
    super(firstName, lastName);
    this.employeeId = employeeId;
  }
 
  describe() {
    return `${super.displayName()} (ID: ${this.employeeId})`; 
  }
}

let employee = new Employee("Martin", "Micunda", 1234);
console.log(employee.describe()); // Martin Micunda (ID: 1234)

Inheritance

Class Inheritance

Prototypal Inheritance

Screenshots taken from Kyle Simpson’s Advanced JavaScript course on Frontend Masters

Classes Free Object

Kyle Simpson OLOO

Classes Free Object

Eric Elliot Factories

Classes Free Object

Douglas Crockford

function person(spec) {
  // initialize own members from spec
  let {firstName, lastName} = spec;









}
let person = person({firstName:'Martin', lastName: 'Micunda');




function person(spec) {
  // initialize own members from spec
  let {firstName, lastName} = spec;
  // members methods
  let displayName = function() { 
    return `${firstName} ${lastName}`;
  };





}
let person = person({firstName:'Martin', lastName: 'Micunda');
 

function person(spec) {
  // initialize own members from spec
  let {firstName, lastName} = spec;
  // members methods
  let displayName = function() { 
    return `${firstName} ${lastName}`;
  };
  // expose public API
  return Object.freeze({ 
          firstName,
          displayName
      });
}
let person = person({firstName:'Martin', lastName: 'Micunda');
console.log(person.displayName()); // Martin Micunda 

 

function person(spec) {
  // initialize own members from spec
  let {firstName, lastName} = spec;
  // members methods
  let displayName = function() { 
    return `${firstName} ${lastName}`;
  };
  // expose public API
  return Object.freeze({ 
          firstName,
          displayName
      });
}
let person = person({firstName:'Martin', lastName: 'Micunda');
console.log(person.displayName()); // Martin Micunda 

function employee(spec) {
  let {employeeId} = spec;
 











}
let martin = employee({firstName:'Martin', lastName: 'Micunda', employeeId: 1234});

function person(spec) {
  // initialize own members from spec
  let {firstName, lastName} = spec;
  // members methods
  let displayName = function() { 
    return `${firstName} ${lastName}`;
  };
  // expose public API
  return Object.freeze({ 
          firstName,
          displayName
      });
}
let person = person({firstName:'Martin', lastName: 'Micunda');
console.log(person.displayName()); // Martin Micunda 

function employee(spec) {
  let {employeeId} = spec;
  // composition with other objects
  // you can select only the parts that you want to use
  // you only "inherit" the stuff that you need
  let {displayName} = person(spec);
  







}
let martin = employee({firstName:'Martin', lastName: 'Micunda', employeeId: 1234});

function person(spec) {
  // initialize own members from spec
  let {firstName, lastName} = spec;
  // members methods
  let displayName = function() { 
    return `${firstName} ${lastName}`;
  };
  // expose public API
  return Object.freeze({ 
          firstName,
          displayName
      });
}
let person = person({firstName:'Martin', lastName: 'Micunda');
console.log(person.displayName()); // Martin Micunda 

function employee(spec) {
  let {employeeId} = spec;
  // composition with other objects
  // you can select only the parts that you want to use
  // you only "inherit" the stuff that you need
  let {displayName} = person(spec);
  
  let describe = function() {
    return `${displayName()} (ID: ${employeeId})`; 
  }




}
let martin = employee({firstName:'Martin', lastName: 'Micunda', employeeId: 1234});

function person(spec) {
  // initialize own members from spec
  let {firstName, lastName} = spec;
  // members methods
  let displayName = function() { 
    return `${firstName} ${lastName}`;
  };
  // expose public API
  return Object.freeze({ 
          firstName,
          displayName
      });
}
let person = person({firstName:'Martin', lastName: 'Micunda');
console.log(person.displayName()); // Martin Micunda 

function employee(spec) {
  let {employeeId} = spec;
  // composition with other objects
  // you can select only the parts that you want to use
  // you only "inherit" the stuff that you need
  let {displayName} = person(spec);
  
  let describe = function() {
    return `${displayName()} (ID: ${employeeId})`; 
  }
  return Object.freeze({ 
          employeeId,
          describe
      });
}
let martin = employee({firstName:'Martin', lastName: 'Micunda', employeeId: 1234});
console.log(employee.describe()); // Martin Micunda (ID: 1234)

Classes Free Object Resources

Modules

  • The replacement for the AMD and CommonJS module formats.

  • The replacement for the AMD and CommonJS module formats.

  • Compact like CommonJS, async like AMD

  • The replacement for the AMD and CommonJS module formats.

  • Compact like CommonJS, async like AMD

  • The ES6 Specification defines two primary module features:

    • module syntax

    • module loader

Modules - Syntax

//------ lib.js ------
export const sqrt = Math.sqrt;
export function square(x) {
    return x * x;
}
export function diag(x, y) {
    return sqrt(square(x) + square(y));
}

Named exports (several per module)

Modules - Syntax

//------ lib.js ------
export const sqrt = Math.sqrt;
export function square(x) {
    return x * x;
}
export function diag(x, y) {
    return sqrt(square(x) + square(y));
}

//------ main.js ------
import { square, diag } from 'lib';
console.log(square(11)); // 121
console.log(diag(4, 3)); // 5

Named exports (several per module)

Modules - Syntax

//------ lib.js ------
export const sqrt = Math.sqrt;
export function square(x) {
    return x * x;
}
export function diag(x, y) {
    return sqrt(square(x) + square(y));
}

//------ main.js ------
import { square, diag } from 'lib';
console.log(square(11)); // 121
console.log(diag(4, 3)); // 5

//------ main.js ------
import * as lib from 'lib';
console.log(lib.square(11)); // 121
console.log(lib.diag(4, 3)); // 5

Named exports (several per module)

Modules - Syntax

//------ lib.js ------
export const sqrt = Math.sqrt;
export function square(x) {
    return x * x;
}
export function diag(x, y) {
    return sqrt(square(x) + square(y));
}

//------ main.js ------
import { square, diag } from 'lib';
console.log(square(11)); // 121
console.log(diag(4, 3)); // 5

//------ main.js ------
import * as lib from 'lib';
console.log(lib.square(11)); // 121
console.log(lib.diag(4, 3)); // 5

Named exports (several per module)

Default exports (one per module)

//------ myFunc.js ------
export default function () { ... };

Modules - Syntax

//------ lib.js ------
export const sqrt = Math.sqrt;
export function square(x) {
    return x * x;
}
export function diag(x, y) {
    return sqrt(square(x) + square(y));
}

//------ main.js ------
import { square, diag } from 'lib';
console.log(square(11)); // 121
console.log(diag(4, 3)); // 5

//------ main.js ------
import * as lib from 'lib';
console.log(lib.square(11)); // 121
console.log(lib.diag(4, 3)); // 5

Named exports (several per module)

Default exports (one per module)

//------ myFunc.js ------
export default function () { ... };

//------ main1.js ------
import myFunc from 'myFunc';
myFunc()

Modules - Syntax

//------ lib.js ------
export const sqrt = Math.sqrt;
export function square(x) {
    return x * x;
}
export function diag(x, y) {
    return sqrt(square(x) + square(y));
}

//------ main.js ------
import { square, diag } from 'lib';
console.log(square(11)); // 121
console.log(diag(4, 3)); // 5

//------ main.js ------
import * as lib from 'lib';
console.log(lib.square(11)); // 121
console.log(lib.diag(4, 3)); // 5

Named exports (several per module)

Default exports (one per module)

//------ myFunc.js ------
export default function () { ... };

//------ main1.js ------
import myFunc from 'myFunc';
myFunc()

//------ MyClass.js ------
export default class { ... };

//------ main2.js ------
import MyClass from 'MyClass';
let inst = new MyClass();

Modules - Loader

Modules - Loader

  • supports conditionally loading modules

Modules - Loader

  • supports conditionally loading modules
  • System.import returns a promise

 

Modules - Loader

  • supports conditionally loading modules
  • System.import returns a promise

 

//------ module.js ------
export function square(x) {
  return x * x;
}

//------ index.js ------
System.import('module')
  .then(module => {
    console.log(module.square(5));
  })
  .catch(error => {
    console.log(error);
  });
 

Features Not-Covered

  • Unicode
  • Symbols
  • Array, Object, Math, Number, String APIs Additions
  • Proxies
  • Reflect API
  • Tail Call Optimization
  • WeakMap, WeakSet
  • Iterators

Use ES6 Today?

  • It may take years for all the features in ES6 to be supported 
  • Use a transpiler to get comfortable with new features sooner and allow writing more expressive code now
  • ES6 compatibility table

 

 

Always bet on JS

Books

Resources

ES 2016 (ES7)

Will include:

  • async and await keywords
  • type annotations (like TypeScript) 
  • Object.observe() method
  • array comprehensions
  • generator comprehensions
  • more ...

Thanks!

Questions?