A Browse Through ES2016

A Browse Through ES2016

Jez Higgins

Illuminated By Monitor Light

A Browse Through ES6

  • A Sprint Through ES6

  • An ES6 Spike

You Should Leave If …​

  • JavaScript isn’t your thing

  • JavaScript is your thing and you’re ES6ed up already

Perhaps Stay If …​

  • JavaScript is your thing

How Did We Get Here?

How Did We Get Here?

The web was a greyer place in 1995

How Did We Get Here?

Brendan Eich

How Did We Get Here?

Jesse James Garrett of Adaptive Path

How Did We Get Here? By Accident

Sam has a low opinion of JavaScript
And so do someother people

JavaScript Oopsys

Weird scoping - Wacky type conversion - Numbers are IEEE 754 double precision floats, but you can do bitwise operations on them as if they were 32 bit integers - Everything’s visible - Functions do triple duty - What is this? - Semicolon insertion - Prototype inheritance - Wait? Did you just go global? - Equality…​

Equals, right

If Type(x) is the same as Type(y), then
    If Type(x) is Undefined, return true.
    If Type(x) is Null, return true.
    If Type(x) is Number, then
        If x is NaN, return false.
        If y is NaN, return false.
        If x is the same Number value as y, return true.
        If x is +0 and y is −0, return true.
        If x is −0 and y is +0, return true.
        Return false.
    If Type(x) is String, then return true if x and y are exactly
        the same sequence of characters (same length and same characters
	in corresponding positions).
	Otherwise, return false.
    If Type(x) is Boolean, return true if x and y are both true or
	  both false. Otherwise, return false.
    Return true if x and y refer to the same object. Otherwise, return false.
If x is null and y is undefined, return true.
If x is undefined and y is null, return true.
If Type(x) is Number and Type(y) is String,
    return the result of the comparison x == ToNumber(y).
If Type(x) is String and Type(y) is Number,
    return the result of the comparison ToNumber(x) == y.
If Type(x) is Boolean,
    return the result of the comparison ToNumber(x) == y.
If Type(y) is Boolean,
    return the result of the comparison x == ToNumber(y).
If Type(x) is either String or Number and Type(y) is Object,
    return the result of the comparison x == ToPrimitive(y).
If Type(x) is Object and Type(y) is either String or Number,
    return the result of the comparison ToPrimitive(x) == y.
Return false.

But now we have ES6

  • ES6 fixes none of this

  • All our crappy JavaScript will continue to run

  • Our new JavaScript can be less crappy

Block Scope

{
  let x = 0;
  // ---
}

Block Scope

{
  let x = 0;
  // ---
}
console.log(x); // ERROR!
{
  console.log(x); // ERROR
  // ---
  let x = 0;
  // ---
}

Block Scope

{
  let x = 0;
  // ---
}

Block Scope

let x = 0;
// ---
{
  let x = "something else";
  // ---
}
// ---

Block Scope

let x = "Hello";

const y = "World";

x = "Goodbye";

Block Scope

let x = "Hello";

const y = "World";

x = "Goodbye";

y = "Cruel World";  // ERROR

For loops

for (let i = 0; i != 10; ++i) {
  // ---
}
// ---

For loops

const arr = [2, 4, 6, 8, 10];

for (let i in arr) {
  let v = arr[i];
  // ---
}

For loops

const arr = [2, 4, 6, 8, 10];

for (const i in arr) {
  let v = arr[i];
  // ---
}

For loops

const arr = [2, 4, 6, 8, 10];

for (const v of arr) {
  // ---
}

For loops

const arr = [2, 4, 6, 8, 10];

for (const [i, v] of arr.entries()) {
  // ---
}

Iterators

let m = new Map();
m.set("hat", "Red");
m.set("flavour", "Mint");

for (const [k, v] of m) {
  // ---
}

Iterators

const twoTimesTable = {
  [Symbol.iterator]: function() {
    let n = 1;
    return {
      next: function() {
        if (n > 10) return { done: true };
        const p = 2*n;
        ++n;
        return { done: false, value: p };
      }
    }
  }
}

for (const p of twoTimesTable)
  console.log(p);

Generators

const twoTimesTable = {
  [Symbol.iterator]: function*() {
    for (let n = 1; n <= 10; ++n)
      yield 2*n;
  }
}

for (const p of twoTimesTable)
  console.log(p);

Generators

const twoTimesTable = {
  *[Symbol.iterator]() {
    for (let n = 1; n <= 10; ++n)
      yield 2*n;
  }
}

for (const p of twoTimesTable)
  console.log(p);

Generators

function* twoTimesTable() {
  for (let n = 1; n <= 10; ++n)
    yield 2*n;
}

for (const p of twoTimesTable())
  console.log(p);

Stand aside Lodash

const arr = [1,2,3,4,5, ........, n];

const first_even_number =
    from(arr).filter(n => n%2==0).first();

Stand aside Lodash

function* randomInterval(min, max) {
    for(;;)
	yield Math.floor(Math.random()*(max-min+1)+min);
}

const first_even_number =
    from(randomInterval(50, 150)).filter(n => n%2==0).first();

Stand aside Lodash (@robsmallshire remix)

function* lucas_sequence() {
    let a = 2, b = 1;
    yield a;
    while (true) {
        yield b;
        [a, b] = [b, a + b];
    }
} // lucas_sequence

const first_six_digit_lucas_number =
    from(lucas_sequence()).filter(n => n.toString().length == 6).first();

Coroutines ala Smallshire

function* async_search(iterable, async_predicate) {
    for (const item of iterable)
        if (yield* async_predicate(item))
            return item;
    return null;
} // async_search

function* lucas_sequence() {
    let a = 2, b = 1;
    yield a;
    while (true) {
        yield b;
        [a, b] = [b, a + b];
    }
} // lucas_sequence

function* async_is_prime(x) {
    if (x < 2)
        return false;
    for (const i of int_range(2, Math.sqrt(x) + 1)) {
        if (x % i == 0)
            return false;
        yield* async_sleep(0);
    }
    return true;
} // async_is_prime

function* int_range(from, to) {
    for (let i = from; i <= to; ++i)
        yield i;
} // int_range

function* async_print_matches(iterable, async_predicate) {
    for (const item of iterable) {
        const matches = yield* async_predicate(item);
        if (matches)
            console.log(`Found ${item}`)
    }
} // async_print_matches

function* async_repetitive_message(message, interval_seconds) {
    while (true) {
        yield* async_sleep(interval_seconds);
        console.log(message);
    } //
} // async_repetitive_message

function* async_sleep(interval_seconds) {
    const expiry = Date.now() + (interval_seconds * 1000);
    while (true) {
        yield;
        const now = Date.now();
        if (now >= expiry)
            break;
    }
} // async_sleep

///////////////////////////////////
class scheduler {
    constructor() {
        this.id_ = 0;
        this.tasks_ = []
        this.monitor_ = null;
    }

    add_task(generator) {
        this.tasks_.push({id: this.id_++, worker: generator});
    }

    run() {
        while (this.tasks_.length != 0) {
            const task = this.tasks_[0];
            this.tasks_ = this.tasks_.slice(1);

            const step = task.worker.next();
            if (step.done)
                console.log(`Task ${task.id} completed with exit value: ${step.value}`);
            else
                this.tasks_.push(task);
        } // while
    } // run
} // scheduler

////////////////////////////////////
const loop = new scheduler();

loop.add_task(async_print_matches(lucas_sequence(), async_is_prime));
loop.add_task(async_repetitive_message("...", 2));

loop.run();

Destructuring

const arr = [2, 4, 6, 8, 10];

for (const [i, v] of arr.entries()) {
  //---
}

Destructuring

const hand = [ { suit: 'Spades', card: 'Ace' },
               { suit: 'Hearts', card: 'Jack' },
	       { suit: 'Clubs',  card: 'Ten' } ];

Destructuring

const hand = [ { suit: 'Spades', card: 'Ace' },
               { suit: 'Hearts', card: 'Jack' },
	       { suit: 'Clubs',  card: 'Ten' } ];

const [ first, second, third ] = hand;

Destructuring

const hand = [ { suit: 'Spades', card: 'Ace' },
               { suit: 'Hearts', card: 'Jack' },
	       { suit: 'Clubs',  card: 'Ten' } ];

const [ first ] = hand;

Destructuring

const hand = [ { suit: 'Spades', card: 'Ace' },
               { suit: 'Hearts', card: 'Jack' },
	       { suit: 'Clubs',  card: 'Ten' } ];

const [ first ] = hand;

const [, , third ] = hand;

Destructuring

const hand = [ { suit: 'Spades', card: 'Ace' },
               { suit: 'Hearts', card: 'Jack' },
	       { suit: 'Clubs',  card: 'Ten' } ];

const [ first ] = hand;

const { suit: s, card: c } = first;

Destructuring

const hand = [ { suit: 'Spades', card: 'Ace' },
               { suit: 'Hearts', card: 'Jack' },
	       { suit: 'Clubs',  card: 'Ten' } ];

const [ first ] = hand;

const { suit, card } = first;

Destructuring

const hand = [ { suit: 'Spades', card: 'Ace' },
               { suit: 'Hearts', card: 'Jack' },
	       { suit: 'Clubs',  card: 'Ten' } ];

const [ { suit, card } ] = hand;

Destructuring

const hand = [ { suit: 'Spades', card: 'Ace' },
               { suit: 'Hearts', card: 'Jack' },
	       { suit: 'Clubs',  card: 'Ten' } ];

const { length: len } = hand;

Destructuring

const [w,x,y,z] = "abcdef";

Destructuring

const [head, ...tail] = "abcdef";

Destructuring

const [head, ...tail] = "abcdef";

// head = 'a'
// tail = ['b','c','d','e','f'];

Destructuring

const { code, msg = 'No error message available' } = response

Parameters

function engageWarpSpeed(factor = max()) {
  // ---
}

Parameters

function engageWarpSpeed(factor = max()) {
  // ---
}

engageWarpSpeed(3);

Parameters

function engageWarpSpeed(factor = max()) {
  // ---
}

engageWarpSpeed(3);

engageWarpSpeed();

Parameters

function format(pattern, ...params) {
  // ---
}

Parameters

function format(pattern, ...params) {
  // ---
}

format("Boring string");

format("%d, %d", 1, 2);

Parameters

function waggleArm({ extension: 0, rotation: 180, speed: 1 } = {}) {
  // ---
}

Parameters

function waggleArm({ extension: 0, rotation: 180, speed: 1 } = {}) {
  // ---
}

waggleArm({ extension: 100, rotation: 90, speed: 5 });

waggleArm({ rotation: 270 });

waggleArm({});

waggleArm();

Parameters

let numbers = [35, 39, 11, 9];

const smallest = Math.min(...numbers);

Parameters

let numbers = [35, 39, 11, 9];

const smallest = Math.min(...numbers);

const alsoSmallest = Math.min(35, 39, 11, 9);

Arrow Functions

const arr = [1, 2, 3];

const cubes = arr.map(x => x*x*x);

Arrow Functions

const arr = [1, 2, 3];

const cubes = arr.map(x => x*x*x);

cubes.forEach(x => { console.log(x); });

Arrow Functions

function Multiplier(factor) { this.factor = factor; }

Multiplier.prototype.multiplyArray = function(arr) {
  var that = this;
  return arr.map(function (x) {
    return that.factor * x;
  });
};

Arrow Functions

function Multiplier(factor) { this.factor = factor; }

Multiplier.prototype.multiplyArray = function(arr) {
  return arr.map(function (x) {
    return this.factor * x;
  }, this);
};

Arrow Functions

function Multiplier(factor) { this.factor = factor; }

Multiplier.prototype.multiplyArray = function(arr) {
  return arr.map(function (x) {
    return this.factor * x;
  }.bind(this));
};

Arrow Functions

function Multiplier(factor) { this.factor = factor; }

Multiplier.prototype.multiplyArray = function(arr) {
  return arr.map(
    function(that) {
      return function (x) {
        return that.factor * x;
      }
    }(this));
};

Arrow Functions

function Multiplier(factor) { this.factor = factor; }

Multiplier.prototype.multiplyArray = function(arr) {
  return arr.map(x => this.factor * x);
};

Arrow Functions

    () => { . . . . }

   (x) => { . . . . }

    x  => { . . . . }

(x, y) => { . . . . }

Arrow

    x => { return x * x; }

    x => x * x

Promises

function handler(request, response) {
  User.get(request.userId,
      user => {
        Notebook.get(user.notebookId,
	    notebook => {
	      doSomethingAsync(notebook,
	        result => response.send(result)
		error => response.send(error);
	    },
	    error => response.send(error);
      },
      error => response.send(err);
  );
}

Promises

function asyncFunc() {
    return new Promise(
        (resolve, reject) => {
            // ---
            resolve(result);
            // ---
            reject(error);
        });
}

asyncFunc()
  .then(result => { ... })
  .catch(error => { ··· });

Promises

asyncFunc()
  .then(result1 => {
    return anotherAsyncFunction(result1);
  })
  .then(result2 => {
    // ---
  })
  .catch(error => {
    // ---
  });

Promises

function handler(request, response) {
  User.get(request.userId)
    .then(user => Notebook.get(user.notebookId))
    .then(notebook => doSomethingAsync(notebook))
    .then(result => response.send(result))
    .catch(error => response.send(error));
}

Promises

Promise.all([
  asyncFunc1();
  asyncFunc2();
])
.then(([result1, result2]) => {
  // ---
})
.catch(error => {
  // ---
});;

Promises

function httpGet(url) {
  return new Promise((resolve, reject) => {
      const request = new XMLHttpRequest();
      request.onload = function() {
        if (this.status === 200)  // Success
          resolve(this.responseText);
        else                      // Something went wrong (404 etc.)
          reject(new Error(this.statusText));
      };
      request.open('GET', url);
      request.send();
    });
}
httpGet('http://www.jezuk.co.uk/')
  .then(body => console.log(body))
  .catch(reason => console.error(`OOPS: ${reason}`));

And the rest …​

Array Methods

Array.of

Array.prototype.map
Array.prototype.filter
Array.prototype.reduce
Array.prototype.includes
Array.prototype.find
Array.prototype.findIndex

Array Methods

const data = Array.of(2, 3, 5, 7, 11);
                                         // [2, 3, 5, 7, 11]
const doubled = data.map(n => n*2);
                                         // [4, 6, 10, 14, 22]
const smallish = data.filter(n => n<7);
                                         // [2, 3, 5]
const total = data.reduce((acc, n) => acc+n, 0);
                                         // 28
const biggerThanFive = data.find(n => n>5);
                                         // 7
const idx = data.findIndex(n => n > 5);
                                         // 3

Object Methods

Object.keys
Object.values
Object.entries

Object Methods

const accu_member = {
  number: '03028',
  name: 'Jez Higgins',
  city: 'Birmingham',
}

for (const key of Object.keys(accu_member))
  console.log(key)

for (const value of Object.values(accu_member))
  console.log(value)

for (const [key, value] of Object.entries(accu_member))
  console.log(`${key} = ${value}`)

Template literals

let event = "accu2017";

let msg = `Hello everyone at ${event}`;

Class definitions

class View extends Component {
  constructor(selector) {
    super(selector);
    // ---
  }
  render(surface) {
    // ---
  }
  get visible() {
    // ---
  }
  static allViews() {
    // ---
  }
}

Modules

// lib/maths.js
export function mult(x y) {
  return x + y;
}

// app.js
import sum from "lib/maths";

let theAnswer = sum(5, 8);

And the rest …​

  • Additional library methods - maths, numbers, strings, arrays, objects

  • Binary and octal numeric literals

  • Regular expressions

  • Subclassable Built-ins

  • Unicode

  • Tail calls

  • Proxies and reflection

  • Module loaders

Using ES6 Today

  • You can use it today

  • Transpiling through Babel

  • Natively with Node

  • You should use it today

Into The Future

  • ES2016 is already here

  • ES2017 is almost here

Async/Await

async function(request, response) {
  try {
    const user = await User.get(request.userId);
    const notebook = await Notebook.get(user.notebookId);
    response.send(await doSomethingAsync(notebook));
  } catch(err) {
    response.send(err);
  }
}

Into The Future

  • ES2016 is already here

  • ES2017 is almost here

  • ES2018 is on the move

  • And so is ES2019

THANKS

  • JavaScript has been profoundly changed by ES6

  • It makes our lives easier

  • Which will make our programs better

Jez UK

Jez Higgins

@jezhiggins

Further reading