ES6/ES2015

Crash Course

let / const

are the new var

let - Better scoping

function varTest() {
  var x = 1;
  if (true) {
    // same variable!
    var x = 2;
    console.log(x); // 2
  }
  console.log(x); // 2
}
function letTest() {
  let x = 1;
  if (true) {
    // different variable
    let x = 2;
    console.log(x); // 2
  }
  console.log(x); // 1
}

let - Better scoping

var a = b + 1;
var b = 2;
console.log(a);
// NaN
let a = b + 1;
let b = 2;
console.log(a);
// ReferenceError: b is not defined

const

Not reassignable variables

const a = 2;
a = 100;
// TypeError: Assignment to constant variable.

const

NOT Immutable!

const foo = {
  bar: 2
};
foo.bar = 100;
console.log(foo);
// => { bar: 100 }

Arrow functions

Shorter way to write functions

// ES5 function declaration
function add(a, b) {
  return a + b;
}

// ES6 arrow functions
const add = (a, b) => a + b;

Useful for small operations

array.filter(function(item) {
  return item.value > 100;
})
.map(function(item) {
  return item.value * item.value;
})
.reduce(function(res, value) {
  return res + value;
});

// versus

array
  .filter(item => item.value > 100)
  .map(item => item.value * item.value)
  .reduce((res, value) => res + value);

Arrow functions syntax

// Parens needed when 0 or several parameters
() => 2;
(a, b) => a + b;


// Parens optional with one parameter
f => f(2);
(f) => f(2);

// Implicit return or block statement with explicit return
() => 2
() => { const a = 2; return a; }

// Can't name a function, but can assign to a variable (stack traces :+1:)
function foo() {}
const foo = () => {}

const foo = () => {
  a: 1,
  b: 2
};
foo() // => ?

Arrow functions trap


const foo = () => {
  a: 1,
  b: 2
};
foo()
// => SyntaxError: Unexpected token :

const foo = () => ({
  a: 1,
  b: 2
});

Bound to parent `this`

function foo() {
  this.bar = 1;
  const fn = () => {
    this.bar++;
  };
  fn();
  console.log(this.bar);
  // => 2
}
foo()

Other subtleties

  • Calling with `.call`/`.apply` does not change `this`
  • No `arguments` object
  • Will throw an error when called with `new`

Spread operator

and

Rest parameters

Spread operator

// Passes multiple args to a function
fn(...[1, 2, 3, 'foo']) === fn(1, 2, 3, 'foo');
// Simpler than `apply`
fn.apply(this, [1, 2, 3, 'foo']);


// Concatenates arrays
[1, 2, ...[3, 4, 5], 6]
// => [1, 2, 3, 4, 5, 6]

// ES proposal: merge objects
const object = { a: 1, b: 2 };
{...object, a: 500, z: 26}
// => { a: 500, b: 2, z: 26 }

Rest parameters

Simpler `arguments`

foo(1, 2, 3, 4);

function foo(...args) {
  // args === [1, 2, 3, 4]
}

function foo(head, ...tail) {
  // head === 1
  // tail === [2, 3, 4]
}

// rest parameter is always last
function foo(...allButLast, last) {} // No

Template Literals

No more string concatenation

let type = 'normal';
'This is a ' + type + ' string';

type = 'template literal';
`This is a ${type} string`;
// => This is a template literal string

// Expressions
`Number ${n + 1}`;

Multiline

console.log(`
  Lorem ipsum dolor sit amet,
  eum ea mucius causae reprimique,
  quo iusto nominati id.
`);
//
//  Lorem ipsum dolor sit amet,
//  eum ea mucius causae reprimique,
//  quo iusto nominati id.
//

As functions

function format(template, ...expressions) {
    console.log(template);
    console.log(expressions);
    return {a: 1};
}

const food = 'Fish & Chips', n = 3;

format`Eat ${food} ${n} times a week`
// [ 'Eat ', ' ', ' times a week' ]
// [ 'Fish & Chips', 3 ]
// => {a: 1}

As functions

// SQL query template
const query = SQL`SELECT * FROM books`
if (params.name) {
  query.append(SQL` WHERE name = ${params.name}`)
}

// GraphQL query template
const query = gql`
  {
    user(id: 5) {
      firstName
      lastName
    }
  }
`

Destructuring

Get field values from objects

const object = {
  a: 1,
  b: 2
};

const {a} = object;
console.log(a);
// => 1

// Equivalent to
const a = object.a;

// Multiple destructuration
const {a, b, c} = object;
console.log(a, b, c);
// => 1, 2, undefined

More in depth

// Rename fields - key AS name
const {a: variable} = object;

// Nesting
const {a: {b}} = {a: {b: 100}}
// === const b = 100;

// Will error as usual when accessing undefined values
const {c: {b}} = {a: {b: 100}} // BOOM

// Works for parameters too
const shoutUserName = ({name}) => name.toUpperCase();
const [a, b] = [1, 2];
// a = 1, b = 2;

// Skip elements
const [a, , b] = [1, 2, 3, 4, 5];
// a = 1, b = 3;

// With spread operator
const [a, ...rest] = [1, 2, 3, 4, 5];
// a = 1, rest = [2, 3, 4, 5];

// Swap values
[a, b] = [b, a]

Destructuring with arrays

Object literal syntax & computed property names

// Object literal syntax
const foo = 1, bar = 2;
const object = { foo, bar };
// Equivalent to
const object = { foo: foo, bar: bar };

// Also for functions
const object = {
  bar() {
    // do stuff
  }
}
object.bar();

Object literal syntax

// Computed property names
const key = 'ok';
const object = {
  [key]: 100;
};

// Equivalent to
const object = {};
object[key] = 100;

Computed property names

Iterators

Iterators

const iterator = ...;

iterator.next(); // {value: 1, done: false};
iterator.next(); // {value: 2, done: false};
iterator.next(); // {value: 3, done: false};
iterator.next(); // {done: true};
iterator.next(); // {done: true};
// ad vitam æternam...

Iterators

const array = [];
for (let i of iterator) {
  array.push(i);
}
array
// [1, 2, 3]

[...iterator]
// [1, 2, 3]

for loops

for (let i = 0; i < a.length; i++) {} // Manual

for (let i of iterator) {} // On iterators

// On keys of an object / items of an array
for (let key in object) {}

for each (let value in object) {} // On values of an object

Create your own iterator?

const object = {
  __iterator__() {
     // ...
  }
};

// What if someone overrides that field...?

Symbols

Symbols

const object = {
  array: [3],
  [Symbol.iterator]() {
    let nextIndex = 0;
    return {
       next() {
           if (nextIndex < this.array.length) {
             return {value: array[nextIndex++], done: false}
           }
           return {done: true};
       }
    };
  }
};

object.iterator // undefined
object[Symbol.iterator] // the iterator
for(let i of object) {
   // ...
}

Symbols

const times2Symbol = Symbol('times2');
const object = {
  n: 3,
  [times2Symbol]() {
    return this.n * 2;
  }
};

object.times2Symbol; // undefined
object[times2Symbol](); // 6

Generators

Functions that return multiple values

function* generator() {
  yield 1;
  yield 2;
  return 3;
}

const g = generator();
g.next(); // { value: 1, done: false }
g.next(); // { value: 2, done: false }
g.next(); // { value: 3, done: true }
g.next(); // { value: undefined, done: true }

Functions that return multiple values

function* generator() {
  yield 1;
  console.log(100);
  yield 2;
  console.log(200);
}

for (let item of generator()) {
  console.log(item);
}
// 1
// 100
// 2
// 200

Functions that return multiple values

function* generator() {
  yield 1;
  yield 2;
  return 3;
}

[...generator()] // [1, 2]

Proxies

const object = {};
const handler = {};
const proxy = new Proxy(object, handler);

proxy.a = 2;
console.log(object);
// => {a: 2}

Proxies

const object = {
  a: 50
};

const handler = {
  get(target, key) {
    if (target[key] === undefined) {
      return 100;
    }
    return target[key];
  }
};

const proxy = new Proxy(object, handler);
proxy.a // => 50
proxy.b // => 100

Proxies

const object = {
  a: 50
};

const handler = {
  set(target, key, value) {
    if (value >= 0) {
      target[key] = value;
      return true;
    }
    return false;
  }
};
const proxy = new Proxy(object, handler);
proxy.a = -5
proxy.a // => 50

Proxies

const array = [1, 2, 3, 4, 5];

const handler = {
  get(target, index) {
    index = parseInt(index);
    if (index < 0) {
      index = target.length + index;
    }
    return target[index];
  }
};

const proxy = new Proxy(array, handler);
proxy[-1] // => 5
proxy[-2] // => 4

Proxies

Map / Set
WeakMap / WeakSet

const user = {
  name: 'Some Name'
};

const userAgeMap = new Map()
userAgeMap.set(user, 20);
// => Map { { name: 'Some Name' } => 20 }

userAgeMap.get(user);
// => 20

[...userAgeMap]
// => [ [ { name: 'Some Name' }, 20 ] ]

Map

const user = {
  name: 'Some Name'
};

const set = new Set();
set.add(user);
set.add(1);
set.add(user);

set.has(user);
// => true

[...set]
// => [ { name: 'Some Name' }, 1 ]

Set

var map = new WeakMap()

// Only references, no primitives
map.set(1, 2)
// TypeError: 1 is not an object!

// Items can be removed by the garbage collector

WeakMap / WeakSet

Mo mo modules!

Static dependencies

import foo from './foo.js';

import {bar, baz} from './foo.js'

import fs, {readFile, writeFile as wf} from 'fs';

// foo.js

export default function() {}

export const baz = 2;

export {
  bar: 1,
  bloo: 2
};

Module import

// a.js
export {
  foo: 2
};

// b.js

// Wrong
import A from './a.js';
A.foo()

// Right
import {foo} from './a.js';
foo()

Module

// Always top-level
import fs from 'fs';

// No conditional
if (condition) {
  import x from 'x'; // BOOM
}

// No variables (no loops)
import x from importName; // BOOM

Module constraints

And a lot more

Native Promise

// Create a new Promise from scratch
new Promise(function(resolve, reject) {
  resolve('foo');
})
.then(function(value) { ... })
.catch(function(error) { ... });

// Resolve with value `foo`
Promise.resolve('foo');
// Reject with error `foo`
Promise.reject(new Error('foo'));
// Resolve when all promises have resolved
Promise.all([promises]);
// Resolve when the first promises resolves
Promise.race([promises]);
function foo(a=1) {}
(a=1) => a + 1;

// Complex stuff!
function foo({ a=1, b=2 } = {a: 3}) {
  console.log(a, b);
}

foo() // 3, 2
foo({}) // 1, 2
foo({a: 100}) // 100, 2

Default parameter value

class Ship extends Mothership {
  constructor() {
    this.message = 'classes';
  }

  // No commas!

  print() {
    return `I don't like ${this.message}.`;
  }
}

Classes in Java(Script)

Object.assign({}, {a: 1, b: 2}, {a: 100})
// => {a: 100, b: 2}

array.find((n) => n === 2);

array.findIndex((n) => n === 2);

// Iterators
array.values(); // Not in Node v6
array.keys();
array.entries();

New builtin methods

// Already in Node v6
array.includes(2) // === (array.indexOf(2) !== -1)

// Not in Node v6
2 ** 3 // === Math.pow(2, 3)
a **= 2 // === a = Math.pow(a, 2)

// That's it

ES7/ES2016

More reading

ES6 Crash Course

By Jeroen Engels

ES6 Crash Course

  • 730