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)