Tim Doherty
Software Architect at
Explicit literal forms for octal and binary:
Most additions to Math are intended for transpile-to-JS systems, like Emscripten
ES6 introduces two new flags, and three new data properties
/y - sticky flag, anchors search at last index
/u - unicode mode, treats surrogate pairs as a single character
const TOKEN_G = /\s*(\-|[0-9]+)\s*/g; const TOKEN_GY = /\s*(\-|[0-9]+)\s*/gy; function tokenize(TOKEN_REGEX, str) { var result = [], match; while (match = TOKEN_REGEX.exec(str)) { result.push(match[1]); } return result; } tokenize(TOKEN_G, '1-2'); // ['1', '-', '2'] tokenize(TOKEN_GY, '1-2'); // ['1', '-', '2'] tokenize(TOKEN_G, '1x-2'); // ['1', '-', '2'] tokenize(TOKEN_GY, '1x-2'); // ['1']
'\uD83D\uDCA9' // 💩 U+1F4A9 PILE OF POO /\uD83D/.test('\uD83D\uDCA9') // true /\uD83D/u.test('\uD83D\uDCA9') // false /\uD83D\uDCA9/u.test('\uD83D\uDCA9') // true /\u{1F4A9}/u.test('\uD83D\uDCA9') // true
flags: the flags used when searching
/xyz/ig.flags // 'gi'
sticky: was the sticky flag specified
/xyz/y.sticky // true
unicode: was the unicode flag specified
/xyz/u.unicode // true
ES6 introduces new data structures, Set and Map
Garbage collection-friendly variants, WeakSet and WeakMap
A collection of unique values of any type
Similar to array, except values are unique and not indexed
let s = new Set(); s.add(1); // Set {1} s.add(2); // Set {1, 2} s.add(3); // Set {1, 2, 3} s.size; // 3 s.has(3); // true s.add(3); // Set {1, 2, 3} s.delete(3); // Set {1, 2} s.has(3); // false s.clear(); // Set {} s.size; // 0
// optional iterable argument let s = new Set([1, 2, 3, 4]); s.forEach(value => {}); for (let item of s) {} let a = [...s] // [1, 2, 3, 4]
Simple key/value map, any value can be used as key or value
Objects have been used as maps historically,but there are key advantages to Map
let m = new Map(); let strKey = 'stringy'; let objKey = {}; let fnKey = () => {}; m.set(strKey, `value for 'stringy'`); m.set(objKey, 'value for objKey'); m.set(fnKey, 'value for fnKey'); m.size // 3 m.get(strKey); // "value for 'stringy'" m.get(objKey); // "value for objKey" m.get(fnKey); // "value for fnKey" m.get('stringy'); // "value for 'stringy'" m.get({}); // undefined m.get(() => {}); // undefined
let m = new Map(); m.set(NaN, 'Not a Number'); m.get(NaN) // "Not a Number" let otherNaN = Number('foo'); m.get(otherNaN) // "Not a Number"
// optional iterable argument let m = new Map([ ['k1', 'v1'], ['k2', 'v2'] ]); for (let [key, value] of m) {} for (let key of m.keys()) {} for (let value of m.values()) {} for (let [key, value] of m.entries) {} m.forEach((key, value) => {}); [...m] // [['k1', 'v1'], ['k2', 'v2']]
A WeakSet is collection of weakly held objects
There aren't many use cases, but one is marking objects
let processedObjects = new WeakSet(); function processObject(obj) { // ... do processing processedObjects.add(obj); return obj; } function isProcessed(obj) { return processedObjects.has(obj); }
let wm = new WeakMap(); let el = document.querySelector('.element'); wm.set(el, 'el data'); wm.has(el); // true wm.get(el); // "el data" el.parentNode.removeChild(el); el = null // remove local reference* wm.has(el); // false wm.get(el); // undefined*this is sort of cheating, we can't look up value by 'null' and el is no longer the key we used to insert
const privateData = new WeakMap(); class Classy { constructor(prop1, prop1) { privateData.set(this. { prop1: prop1, prop2: prop2 }); } get prop1() { return privateData.get(this).prop1; } get prop2() { return privateData.get(this).prop2; } }
Module-friendly reflection API, mostly migrated from Object
Most methods map one-to-one onto Proxy traps, up next
Define custom behavior for some operations on an object
can't be transpiledProxy adds intercession to JavaScript
A proxy instance is created with the following parameters:
All traps are optional, default is to forward to the target
let handler = { get(target, property, receiver) { return property in target ? target[property] : 42; } }; let p = new Proxy({foo: 'bar'}, handler); p.foo // 'bar' p.meaningOfLife // 42
let validator = { set(target, property, value, receiver) { if (property === 'year' && !Number.isInteger(value)) { throw new TypeError(); } Reflect.set(target, property, value); } }; let car = new Proxy({}, validator); car.year = 1963 // 1963 car.year = 'foo' // TypeError
let tracer = { get(target, trapName, receiver) { return function (...args) { console.log(trapName.toUppercase() + ' ' + args.slice(1)); return Reflect[trapName](...args); } } }; let p = new Proxy({}, tracer); p.foo = 'bar' // SET foo,'bar',[object Object] p.foo // GET foo,[object Object]
let membrane = { get(target, trapName, receiver) { return function (...args) { // ...threat mitigation return Reflect[trapName](...args); } } }; let rev = Proxy.revocable(jQuery, membrane); let jqMembrane = rev.proxy; jqMembrane.ajax(/*...*); // ...when done rev.revoke() jqMembrane.ajax(/*...*); // TypeError
Execute functions in tail position without growing the call stack
Effectively use recursion as a tool, with stack frame reuse
Only works in strict mode
Last thing a function does is return the result of a function call
"use strict"; function baz(bim) { return bim + 1; } function foo(bar) { bar += 1; return baz(bar); <-PTC } function foo(bar) { bar += 1; return 1 + baz(bar); <-!PTC }
"use strict"; function baz(bim) { return bim + 1; // A } function foo(bar) { bar += 1; return baz(bar); // B } foo(1); // C
"use strict"; function baz(bim) { return bim + 1; // A } function foo(bar) { bar += 1; return baz(bar); // B } foo(1); // C
"use strict"; function baz(bim) { return bim + 1; // A } function foo(bar) { bar += 1; return baz(bar); // B } foo(1); // C
"use strict"; function baz(bim) { return bim + 1; // A } function foo(bar) { bar += 1; return baz(bar); // B } foo(1); // C
"use strict"; function baz(bim) { return bim + 1; // A } function foo(bar) { bar += 1; return baz(bar); // B } foo(1); // C
"use strict"; function baz(bim) { return bim + 1; // A } // just prior to baz() function foo(bar) { bar += 1; return baz(bar); // B } foo(1); // C
function factorial(n) { return facHelper(n, 1); } function facHelper(n, acc) { if (n == 1) { return acc; } else { return facHelper(n-1, n * acc); <-PTC } } factorial(1); // 1 factorial(5); // 120
function forEach(arr, cb, start = 0) { if (0 <= start && start < arr.length) { callback(arr[start], start, arr); return forEach(arr, cb, start+1); <-PTC } } forEach(['a', 'b'], (el, i) => { console.log(`${i}. ${el}`) }); // 1. a // 2. b
ES6 introduces a native Promise object
Promise API eases deferred and asynchronous computations
Influenced by libraries / frameworks like jQuery, Q, AngularJS
let p = new Promise((resolve, reject) => { resolve('foo'); }); p.then(value => { console.log(value); // "foo" });
function asyncFunc1() { return new Promise((resolve, reject) => { //... }); } asycFunc1() .then(asyncFunc2) .then(asyncFunc3) .then(asyncFunc4) .then(asyncFunc5) .catch((reason) => {...});
function httpGet(url) { return new Promise((resolve, reject) => { var request = new XMLHttpRequest(); request.onreadystatechange = () => { if (this.status === 200) { resolve(this.response); } else { reject(new Error(this.statusText)); } } request.onerror = () => { reject(new Error(this.statusText)); }; request.open('GET', url); request.send(); }); }Fetch API
httpGet('http://somesite.com') .then( value => { //success }, reason => { // error } );
let promises = [ 'http://somesite.com/path1', 'http://somesite.com/path2 ].map(httpGet); Promise.all(promises) .then(values => { values.forEach(value => { // do something on success }); }) .catch(reason => { // gets first rejection );
function timeout(ms) { return new Promise((resolve, reject) => { setTimeout(resolve, ms); }); } Promise.race([ httpGet('http://somesite.com'), timeout(5000).then(() => { throw new Error('Timed out') }) ]) .then(value => {...}) .catch(reason => {...});
// from part II function req(url) { doAjax(url, function (response) { it.next(response); }); } function* async() { let r1 = yield req('/req1'); let r2 = yield req(`/req2?id=${r1.id}`); console.log(`r2: ${r2}`); } var it = async(); it.next(); // kick it off ... we can do better, with promises
function spawnGenerator(g) { var it = g(), ret; (function iterate(val){ ret = it.next(val); if (!ret.done) { if ('then' in ret.value) { ret.value.then(iterate); } else { setTimeout(() => { iterate(ret.value); }, 0); } } })(); }"poor man's promise check" and spawn code form Kyle Simpson
function* async() { // use httpGet, which returns a promise let r1 = yield httpGet('/req1'); let r2 = yield httpGet(`/req2?id=${r1.id}`); console.log(`r2: ${r2}`); } spawnGenerator(async);
ECMAScript 6 is now the language standard
Browsers are continually implementing features
Writing/transpiling ES6 now, will help push browser vendors
EcmaScript 2016 (ES7)