EcmaScript 2015

Agenda

  • let and const
  • Symbol
  • Generators
  • SetMapWeakSetWeakMap
  • Tail calls

let and const

block scoped

function func() {
    if (true) {
        let tmp = 123;
    }
    
    // ReferenceError: tmp is not defined
    console.log(tmp); 
}
function func() {
    if (true) {
        var tmp = 123;
    }
    
    // 123
    console.log(tmp);
}

vs

function func(arg) {
    {
        let arg; // shadows parameter `arg`
    }
}

let vs var

in loops

let arr = [];
for (var i=0; i < 3; i++) {
    arr.push(() => i);
}
console.log(arr.map(x => x())); // [3,3,3]
let arr = [];
for (let i=0; i < 3; i++) {
    arr.push(() => i);
}
console.log(arr.map(x => x())); // [0,1,2]

vs

const

immutable (kind of)

const obj = {};
obj.prop = 123;
console.log(obj.prop); // 123

obj = {}; // TypeError
const foo = 'abc';
foo = 'def'; // TypeError
const obj = Object.freeze({});
obj.prop = 123; // TypeError

True const:

Symbol

New primitive type

let symbol1 = Symbol();

> let symbol2 = Symbol('symbol2');
> String(symbol2)
'Symbol(symbol2)'

> symbol1 === symbol2
false

> typeof symbol1
'symbol'

const MY_KEY = Symbol();
let obj = {};

obj[MY_KEY] = 123;
console.log(obj[MY_KEY]); // 123

const FOO = Symbol();
let obj = {
    [FOO]() {
        return 'bar';
    }
};
console.log(obj[FOO]()); // bar

Symbol

use cases

const COLOR_RED    = Symbol();
const COLOR_ORANGE = Symbol();
const COLOR_YELLOW = Symbol();
const COLOR_GREEN  = Symbol();
const COLOR_BLUE   = Symbol();
const COLOR_VIOLET = Symbol();
let obj = {
  data: ['hello', 'world'],
  [Symbol.iterator]() {
    const self = this;
    let index = 0;
    return {
        next() {
            if (index < self.data.length) {
                return {
                    value: self.data[index++]
                };
            } else {
                return { done: true };
            }
        }
    };
  }
};

Generators

functions that can be paused and resumed

Generator

simple example

function *foo() {
    yield 1;
    yield 2;
    yield 3;
}

var it = foo();

console.log( it.next() ); // { value:1, done:false }
console.log( it.next() ); // { value:2, done:false }
console.log( it.next() ); // { value:3, done:false }
console.log( it.next() ); // { value:undefined, done:true }

Implementing iterators

function* objectEntries(obj) {
    let propKeys = Reflect.ownKeys(obj);

    for (let propKey of propKeys) {
        yield [propKey, obj[propKey]];
    }
}
let jane = { first: 'Jane', last: 'Doe' };
for (let [key,value] of objectEntries(jane)) {
    console.log(`${key}: ${value}`);
}
// Output:
// first: Jane
// last: Doe

Implementing iterators

let fibonacci = {
  [Symbol.iterator]: function*() {
    var pre = 0, cur = 1;
    for (;;) {
      var temp = pre;
      pre = cur;
      cur += temp;
      yield cur;
    }
  }
}
for (let n of fibonacci) {
  // truncate the sequence at 1000
  if (n > 1000)
    break;
  console.log(n);
}

Fibonacci as an iterator

Generators...

function* genFunc() {
    yield 'a';
    yield 'b';
}

given a simple generator function:

// for..of loop
for (let x of genFunc()) {
    console.log(x);
}
// Output:
// a
// b

// Spread operator
let arr = [...genFunc()]; // ['a', 'b']

// Destructuring
let [x, y] = genFunc();

Generators...

function* foo() {
    yield 'a';
    yield 'b';
}

yield*

function* bar() {
    yield 'x';
    yield* foo();
    yield 'y';
}
let arr = [...bar()];
// ['x', 'a', 'b', 'y']
function* bar() {
    yield 'x';
    yield* ['a', 'b'];
    yield 'y';
}

Generator

as a tree walker

class BinaryTree {
    constructor(value, left=null, right=null) {
        this.value = value;
        this.left = left;
        this.right = right;
    }

    /** Prefix iteration */
    * [Symbol.iterator]() {
        yield this.value;
        if (this.left) {
            yield* this.left;
        }
        if (this.right) {
            yield* this.right;
        }
    }
}
let tree = new BinaryTree('a',
    new BinaryTree('b',
        new BinaryTree('c'),
        new BinaryTree('d')),
    new BinaryTree('e'));

for (let x of tree) {
    console.log(x);
}

// Output:
// a
// b
// c
// d
// e

Generators

use cases

  • iterators
  • lazy evaluation algorithms
  • file readers
  • promises -> async / await (ES7)

Map

> let map = new Map();

> map.set('foo', 123);
> map.set('foo2', 1234);
> map.get('foo')
123

> map.has('foo')
true
> map.delete('foo')
true
> map.has('foo')
false
> map.size
1
> map.clear();
> map.size
0

Map

let map = new Map();

const KEY1 = {};
map.set(KEY1, 'hello');
console.log(map.get(KEY1)); // hello

const KEY2 = {};
map.set(KEY2, 'world');
console.log(map.get(KEY2)); // world

Setting values as keys!

Map

let map = new Map([
    [false, 'no'],
    [true,  'yes'],
]);

Iterating!

for (let key of map.keys()) {
    console.log(key);
}
for (let entry of map.entries()) {
    console.log(entry[0], entry[1]);
}
for (let value of map.values()) {
    console.log(value);
}
for (let [key, value] of map) {
    console.log(key, value);
}

WeakMap

  • Non-primitive keys
  • No iteration over keys or values
  • No delete() method
  • Keys' references held weakly (for gc)

WeakMap

var map = new Map(); // maps can have object keys
function useObj(obj){
    doSomethingWith(obj);
    var called = map.get(obj) || 0;
    called++; // called one more time
    if(called > 10) report(); // Report called more than 10 times
    map.set(obj, called);
}
var map = new WeakMap(); // create a weak map
function useObj(obj){
    doSomethingWith(obj);
    var called = map.get(obj) || 0;
    called++; // called one more time
    if(called > 10) report(); // Report called more than 10 times
    map.set(obj, called);
}

WeakMap

  • keeping private data about a specific object and only giving access to it to people with a reference to the Map
  • keeping data about host objects like DOM nodes in the browser
  • tracking all the rejected promises

Use cases

Set, WeakSet

> let set = new Set();
> set.add('red')

> set.has('red')
true
> set.delete('red')
true
> set.has('red')
false

Tail-call functions

  • Function calls without growing a stack
  • Have to be last ones in the block
function factorial(x) {
    if (x <= 0) {
        return 1;
    } else {
        return x * factorial(x-1); // (A)
    }
}
function factorial(n) {
    return facRec(n, 1);
}
function facRec(x, acc) {
    if (x <= 1) {
        return acc;
    } else {
        return facRec(x-1, x*acc); // (A)
    }
}

=>

Links

  • http://www.2ality.com
  • http://babeljs.io/docs/learn-es2015
  • http://stackoverflow.com/questions/29413222/what-are-the-actual-uses-of-es6-weakmap
  • https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference

EcmaScript 2015

By Karol Andrusieczko

EcmaScript 2015

  • 864