ES6 IN PRACTICE
TIM DOHERTY
Who am I?
Before we get started...
A brief history...
But the marketing gods had to be appeased…
…and recruiters weren’t confused enough
Script
and they saw that it was good
so they standardized it…
…but what to call this new language?
WebScript?
WebScript?
BrowserScript?
WebScript?
BrowserScript?
SchemeScript?
WebScript?
BrowserScript?
SchemeScript?
EczemaScript?
WebScript?
BrowserScript?
SchemeScript?
ECzeMAScript?
WebScript?
BrowserScript?
SchemeScript?
ECMAScript
ECMAScript 1
ECMAScript 1
ECMAScript 2
ECMAScript 1
ECMAScript 2
ECMAScript 3
ECMAScript 1
ECMAScript 2
ECMAScript 3
ECMAScript 4
ECMAScript 1
ECMAScript 2
ECMAScript 3
ECMAScript 4
ECMAScript 1
ECMAScript 2
ECMAScript 3
ECMAScript 4
ECMAScript 3.1
ECMAScript 1
ECMAScript 2
ECMAScript 3
ECMAScript 4
ECMAScript 3.1
ECMAScript 1
ECMAScript 2
ECMAScript 3
ECMAScript 4
ECMAScript 3.1
ECMAScript 5
ECMAScript 1
ECMAScript 2
ECMAScript 3
ECMAScript 4
ECMAScript 3.1
ECMAScript 5
ECMAScript 2015
Umm… 6?
ECMA-262 6th Edition, ECMAScript 2015
“ES6 is a radical jump forward for the language”
~ Kyle Simpson
“…geared toward solving problems that developers actually face.”
~ Nicholas Zakas
“…adds significant new syntax for writing complex applications”
~ Wikipedia
“ES6 is a large and important update to JavaScript”
~ Wes Bos
“ECMAScript 6 will introduce many great features”
~ Dr. Axel Rauschmayer
“ES6 is a significant update to the language”
~ Luke Hoban
“Now this dream is coming true, not just in ES6 draft specs...”
~ Brendan Eich
“ES6 is a radical jump forward for the language”
~ Kyle Simpson
“…geared toward solving problems that developers actually face.”
~ Nicholas Zakas
“…adds significant new syntax for writing complex applications”
~ Wikipedia
“ES6 is a large and important update to JavaScript”
~ Wes Bos
“ECMAScript 6 will introduce many great features”
~ Dr. Axel Rauschmayer
“ES6 is a significant update to the language”
~ Luke Hoban
“Now this dream is coming true, not just in ES6 draft specs...”
~ Brendan Eich
On Over 100 Ten-Best Lists!
ES6 in Depth
JavaScript Sucks!
“Can’t we just wait for browsers to catch up?”
It’s not 2009 anymore
F
U
D
EAR
NCERTAINTY
OUBT
Now What?
$ npm install --save-dev ui-build
ES6 in Depth
(abridged)
What’s the big deal?
JavaScript
Despite its quirks
JavaScript is the most widely deployed
programming language
Any application that can be written in JavaScript, �will eventually be written in JavaScript.�~Atwood's Law
Syntax
noun | syn·tax | \ˈsin-ˌtaks\
b : the part of grammar dealing with this�
Enhancement
noun | en·hance·ment | \enˈhansmənt\
Feature
noun | fea·ture | \ˈfē-chər\
.updates {� text-decoration: underline !important;�}
Syntax
let foo = 'bar', bar = 'baz';�let obj = {� foo,� bar() {� return this[bar];� },� [bar]: 'bim'�};�obj.foo; // bar�obj.baz; // bim�obj.bar(); // bim
function greet(first='John', last='Doe') {� return `Hello ${first} ${last}`;�}��greet(); // Hello John Doe�greet('Jane'); // Hello Jane Doe�greet('Fred', 'Smith'); //Hello Fred Smith
JavaScript
...
// ES5�function func() {� var args = [].slice.call(arguments);� args.map().reduce().filter().etc();�}��// ES6 rest�function func(...args) {� args.map().reduce().filter().etc();�}
function doTheThing(arg1, arg2, arg3, doIt = true) {� // do the thing�}��function dontDoTheThing(...args) {� args.push(false)� doTheThing.apply(undefined, args);�}��dontDoTheThing(a, b, c);
// doTheThing(a, b, c, false);
var a1 = [1, 4];�var a2 = [2, 3];��// ES5�a1.splice(1, 0, a2); // [1, 2, 3, 4] ��// ES6�var a3 = [1, ...a2, 4]; // [1, 2, 3, 4]
function doTheThing(arg1, arg2, arg3, doIt = true) {� // do the thing�}��function dontDoTheThing(...args) {� doTheThing(...args, false);�}��dontDoTheThing(a, b, c);
// doTheThing(a, b, c, false);
let { a: { b: { c: { d: { e } } } } } = { � a: { � b: { � c: { � d: { � e: 'foo'� } � }� } � } �};�console.log(e); // foo
import { React, Component } from 'react';
import React from 'react';��function MyComponent({ foo, bar, baz }) {� return (� <div>� {foo} {bar} {baz}� </div>� );�}
function fn({foo='bar', bar='baz'} = {}) {� return `foo: ${foo}, bar: ${bar}`;�}��fn(); // foo: bar, bar: baz�fn({foo: 'fooey'}); // foo: fooey, bar: baz
var foo = 'bar';��{� // this is a valid block in ES5� var foo = 'baz';�}��console.log(foo); // 'baz'
let foo = 'bar';��{� let foo = 'baz';� console.log(foo); // 'baz'�}��console.log(foo); // 'bar'
function iterate() {� var i = 0, funcs = [];� for (;i < 5; i++) {� funcs.push(function () {� alert(i);� });� } � return funcs;�}
(iterate()).forEach(function (element) {� element(); // 5�}
function iterate() {� var i =0, funcs = [];� for (;i < 5; i++) {� (function (j) {� funcs.push(function () {� alert(j);� });� })(i);� } � return funcs;�}
function iterate() {� var funcs = [];� for (let i = 0;i < 5; i++) {� funcs.push(function () {� alert(i);� });� } � return funcs;�}
const
const obj = { foo: 'bar' };
obj.bar = 'baz';
console.log(obj);
// Object { foo: 'bar', bar: 'baz' };
const !== immutable
()=>{}
arg => expr��arg => { stmt1; stmt2; ... }��arg => ({})��(arg1, arg2, ...) => expr��(arg1, arg2, ...) => { stmt1; stmt2; ... }��(arg1, arg2, ...) => ({})
[10, 9, 8, 7, 6, 5, 4, 3, 2, 1]� .map(n => n * 3)� .filter(n => n % 2 === 0)� .sort((a, b) => a - b)� .reduce((prev, curr) => prev + curr, 0);
fetch('http://domain.com')� .then(response => response.json())� .then(data => data)� .catch(err => err.message);
const obj = {� foo: 'bar',� method() {� setTimeout(() => {� console.log(this.foo);� }, 0);� }�};��obj.method(); // bar
const multiplier = n => a => a * n;��const double = multiplier(2);�const triple = multiplier(3);��double(3); // 6�triple(3); // 9
console.log(foo()); // bar�console.log(bar()); // TypeError
�function foo() { return 'bar'; }��var bar = () => 'baz';
const obj = {� foo: 'bar'�};��const method = (() => {� console.log(this.foo);�}).bind(obj);��method(); // undefined
Template Literals
// ES5�var url = 'http://' + [host, path, id].join('/');��// ES6�let url = `http://${host}/${path}/${id}`;
// ES5 with Underscore/Lodash
var tpl = _.template('hello <%= name %>!');�var output = tpl({ name: 'fred' });��// ES6�let user = { name: 'fred' };�let output = `hello ${user.name}`;
let view = {� state: { name: 'World' },� render() {� return `� <div>� Hello ${this.state.name}!� </div>� `;� }�};�document.body.innerHTML = view.render();
error quotes Strings must use singlequote
Namespacing
IIFE
JavaScript Module Pattern
CommonJS
AMD
// module1.js�export function one() {}�export function two() {}�export default { one, two };��// module2.js�import module1 from './module1';�import { one, two as three } from 'module1';�import * as oneTwo from 'module1';
HTTP
HTTP
+
Enhancements
let str = 'Hello world!';��// ES5�str.indexOf('Hello') !== -1 // true�str.indexOf('foobar') !== -1 // false��// ES6�str.includes('Hello') // true�str.includes('foobar') // false
let str = 'Hello world!';��// ES5�str.indexOf('Hello') === 0 // true��// ES6�str.startsWith('Hello') // true
let str = 'Hello world!';��// ES5�str.substr(� str.length - 'world!'.length - 1�) === 'world!' // true��// ES6�str.endsWith('world!') // true
Array.from('abc'); // ['a', 'b', 'c']�Array.of('abc'); // ['abc']
let a = [1, 2, 3, 4, 5];�a.find(e => e % 3 === 0); // 3�a.findIndex(e => e % 3 === 0); // 2
[1, 2, 3].fill(4); // [4, 4, 4]�[1, 2, 3].copyWithin(0, 2); // [3, 2, 3]
Number.EPSILON�Number.MAX_SAFE_INTEGER�Number.MIN_SAFE_INTEGER�Number.isNaN()�Number.isFinite()�Number.isInteger()�Number.isSafeInteger()�Number.parseInt()�Number.parseFloat()
�
Math.acosh()�Math.asinh()�Math.atanh()�Math.cosh()�Math.sinh()�Math.tanh()�Math.cbrt()�Math.clz32()�Math.expm1()�Math.fround()�
Math.hypot()�Math.imul()�Math.log10()�Math.log1p()�Math.log2()�Math.sign()�Math.trunc()
let obj1 = { foo: 'bar' };�let obj2 = { bar: 'baz' };
let obj3 = Object.assign({}, obj1, obj2);��Obj3; // { foo: 'bar', bar: 'baz' }
let obj1 = { � foo: 'bar', � baz: [1, 2, 3]�};�let obj2 = Object.assign({}, obj1);��obj2.baz.push(4);�console.log(obj1.baz); // [1, 2, 3, 4]
let obj1 = Object.create({ bar: 'baz' });��obj1.foo = 'bar';��let o1Proto = Object.getPrototypeOf(obj1);�let obj2 = Object.assign(� Object.create(o1Proto),� obj1�);��obj2.bar // 'baz'
let sym = Symbol();�let o = {� [sym]: 'foo',� bar: 'baz'�};��o[sym]; // 'foo'�Object.getOwnPropertyNames(o); // ['bar']�Object.getOwnPropertySymbols(o); // [Symbol()]
let Thing = (function () {� let nameSymbol = Symbol();� return class Thing {� constructor(name) {� this[nameSymbol] = name;� }� get name() {� return this[nameSymbol];� }� };�})();
Iteratables
interface Iterable {� [Symbol.iterator]() : Iterator;�}��interface Iterator {� next() : IteratorResult;� return?(value? : any) : IteratorResult;�}��interface IteratorResult {� value : any;� done : boolean;�}
let arr = [1, 2, 3, 4];�let iterator = arr[Symbol.iterator]();�let result = iterator.next();��while(!result.done) {� console.log(result.value);� result = iterator.next();�}
let arr = [1, 2, 3, 4];��for (let i of arr) {� console.log(i);�}
[...'Hello'[Symbol.iterator]()];
// ['H', 'e', 'l', 'l', 'o']
const myObject = {� [Symbol.iterator]() {� return {� next() {
// your implementation here
}
};
}�};
function* countDown(count) {� while (count--) {� yield count;� }�}��[...countDown(5)]��//[4, 3, 2, 1, 0]
function* even(iterable) {� for (let n of iterable) {� if (n % 2 === 0) { yield n; }� }�}��function* take(n, iterable) {� const iter = iterable[Symbol.iterator]();� while(n--) { yield iter.next().value; }�}��let arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];�[...take(3, even(arr))]; // [2, 4, 6]
function consume(iterable, consumer) {� consumer.next();� for (let x of iterable) { consumer.next(x); }� consumer.return();�}��function* processAndLog() {� while (true) {� const val = yield;� process(val);� logToServer(`Processed ${val} at ${new Date()}`);� }�}��consume([1, 2, 3, 4], processAndLog());
import { call, put } from 'redux-saga/effects'� �import myApi from '_shared/myApi';�import { actions } from '../module';� �export function* getItems({ payload }) {� try {� const response = yield call(myApi.get, payload);� yield put(actions.setData(response.data));� } catch (err) {� yield put(actions.handleFailure(err));� }
}
var obj = { foo: 'bar', baz: 'bim' };��Object.keys(foo);// ['foo', 'baz']��obj.hasOwnProperty('foo'); // true
var obj = { foo: 'bar', baz: 'bim' };��Object.defineProperty(obj, 'fee', {� value: 'fum',� writable: true,� enumerable: true�});
?
// ES5�var obj = { foo: 'bar', baz: 'bim' }, proxy;��Object.keys(obj).forEach(function(key) {� Object.defineProperty(proxy, key, {� get: function () {/*trap here...*/},
set: function (val) {/*trap here...*/}� });�});
let person = new Proxy({}, {� set(target, prop, value) {� if (prop === 'age') {� if (!Number.isInteger(value)) {� throw new TypeError('Age must be a number');� }� }� target[prop] = value;� return true;� }�});��person.age = 47;�console.log(person.age); // 47�person.age = 'foo'; // TypeError
const storage = new Proxy({}, {� get(target, prop, receiver) {
return localStorage.getItem(prop);
},� set(target, prop, value) {
localStorage.setItem(prop, value);
}�});��storage.foo = { bar: 'baz' };�console.log(storage.foo); // { bar: 'baz' };
let sum = (a, b) => a + b;
�let multiplier = (multiple) => ({� apply (target, ctx, args) {� return Reflect.apply(...args) * multiple� }�});��let sumTwice = new Proxy(sum, multiplier(2));�let sumThrice = new Proxy(sum, multiplier(3));�console.log(sumTwice(1, 2)); // 6�console.log(sumThrice(1, 2)); // 9
Reflect.apply()
Reflect.construct()
Reflect.defineProperty()
Reflect.deleteProperty()
Reflect.enumerate()
Reflect.get()
Reflect.getOwnPropertyDescriptor()
Reflect.getPrototypeOf()
Reflect.has()
Reflect.isExtensible()
Reflect.ownKeys()
Reflect.preventExtensions()
Reflect.set()
Reflect.setPrototypeOf()
let obj = { foo: 'bar', _baz: 'bim' };��let {proxy, revoke} = Proxy.revocable(obj, {� set(target, key, value) {� if (key[0] === '_') {� revoke();� }� }�});��proxy.foo = 'fum'; // ok�proxy._baz = 'bar'; // proxy revoked�console.log(proxy._baz); // TypeError
Finally!
Finally!
const p = new Promise((resolve, reject) => {� setTimeout(resolve, 100, 'kept');�});��p.then(/* resolved */)
.catch(/* rejected or error */);��Promise.resolve();�Promise.reject();�Promise.all();�Promise.race();
document.querySelector();�
new Promise((resolve, reject) => { ... });
Syntax
noun | syn·tax | \ˈsin-ˌtaks\
b : the part of grammar dealing with this�
Who cares?
=
ES6 > ES5 > UglifyJS > Webpack > ?
JavaScript still doesn’t have classes
class Foo {� constructor(baz) {� this.bar = baz;� }� foobar() {� return this.bar;� }�}
function Foo(baz) {� this.bar = baz;�}
�Foo.prototype.foobar = function() {� return this.bar;�};
class Bar {
// ...
}
�class Foo extends Bar {
// ...
}
function Bar() {
// ...
}
�function Foo() {
// ...
}
�Foo.prototype = new Bar();
“Sometimes when I'm writing Javascript I want to throw up my hands and say "this is bullshit!" but I can never remember what "this" refers to”
Ben Halpern @bendhalpern
import { Component } from '@angular/core';��@Component({� selector: 'my-app',� template: `<h1>Hello {{name}}</h1>`�})�export class AppComponent { name = 'Angular'; }
class Welcome extends React.Component {� constructor(props) {� super(props);� this.state = { greeting: 'Hello' };� }� render: () => (� <h1>� this.state.greeting, {this.props.name}� </h1>� )�}
Go Native!
$.extend();�_.extend();�angular.extend();��// ES6�Object.assign();
_.find();��// ES6�Array.prototype.find();
_.startsWith();�_.endsWith();�_.includes();*��// ES6�String.prototype.startsWith();�String.prototype.endsWith();�String.prototype.includes();
var tpl = _.template('hello <%= name %>!');�var output = tpl({ name: 'fred' });��// ES6�let user = { name: 'fred' };�let output = `hello ${user.name}`;
var jqPromise = (jQuery.Deferred()).promise();�var qPromise = (Q.defer()).promise;�var ngPromise = ($q.defer()).promise;
var bbPromise = new Bluebird.promise(
function (resolve, reject) {}
);��// ES6�let p = new Promise((resolve, reject) => {});
What’s next?
ES 2016
ES 2016
ES 2017
ES 2017
ES 2018
ES 2019
ES 2020
...and beyond
THANKS!
@TimCDoherty