EcmaScript 2015
Agenda
- let and const
- Symbol
- Generators
- Set, Map, WeakSet, WeakMap
- 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
- 918