Upgrade to Pro — share decks privately, control downloads, hide ads and more …

HTML5DevConf ES7 and Beyond!

HTML5DevConf ES7 and Beyond!

Jeremy Fairbank

October 20, 2015
Tweet

More Decks by Jeremy Fairbank

Other Decks in Programming

Transcript

  1. ES7

  2. TC39 Process • Five stage process for converting an idea

    to a standard feature. • https://tc39.github.io/process- document
  3. Stage 0 - Strawman • Informal idea or proposal for

    change or addition. • Function bind operator 
 • Do expressions foo::bar(); let y = do { let x = 40; x + 2; }
  4. Stage 1 - Proposal • Make a case for addition

    by describing use cases, challenges, high-level API. • Class and property decorators class Foo { @memoize bar() { return 42; } }
  5. Stage 2 - Draft • Precise syntax and semantics; initial

    spec. • Object rest and spread properties let obj = { ...otherObj, x: 'foo' };
  6. Stage 3 - Candidate • Completed spec; needs reviewing for

    refinements. • SIMD - Single instruction, multiple data • Exponentiation operator
 • Array.prototype.includes
 3 ** 2; // 9 [1, 2, 3].includes(2); // true
  7. Stage 3 - Candidate • Async Functions
 async function loadOrders()

    { const orders = await fetchJson('/orders'); console.log(orders); }
  8. Stage 4 - Finished • Ready to be included in

    the next ES standard. • No proposals at Stage 4 at the moment.
  9. Class Decorators • Annotate and modify classes and their properties.

    • Use @ and an expression that evaluates to a function. • Decorator precedes a class or method definition. • github.com/wycats/javascript-decorators
  10. class Person { constructor(firstName, lastName) { this.firstName = firstName; this.lastName

    = lastName; } get fullName() { return `${this.firstName} ${this.lastName}`; } } const p = new Person('Jeremy', 'Fairbank'); for (const key in p) { console.log(`${key}: ${p[key]}`); } // firstName: Jeremy // lastName: Fairbank
  11. class Person { constructor(firstName, lastName) { this.firstName = firstName; this.lastName

    = lastName; } get fullName() { return `${this.firstName} ${this.lastName}`; } } const p = new Person('Jeremy', 'Fairbank'); for (const key in p) { console.log(`${key}: ${p[key]}`); } // firstName: Jeremy // lastName: Fairbank Getters are nonenumerable by default.
  12. class Person { constructor(firstName, lastName) { this.firstName = firstName; this.lastName

    = lastName; } } Object.defineProperty(Person.prototype, 'fullName', { enumerable: true, get() { return `${this.firstName} ${this.lastName}`; } }); const p = new Person('Jeremy', 'Fairbank'); for (const key in p) { console.log(`${key}: ${p[key]}`); } // firstName: Jeremy // lastName: Fairbank // fullName: Jeremy Fairbank
  13. class Person { constructor(firstName, lastName) { this.firstName = firstName; this.lastName

    = lastName; } } Object.defineProperty(Person.prototype, 'fullName', { enumerable: true, get() { return `${this.firstName} ${this.lastName}`; } }); const p = new Person('Jeremy', 'Fairbank'); for (const key in p) { console.log(`${key}: ${p[key]}`); } // firstName: Jeremy // lastName: Fairbank // fullName: Jeremy Fairbank
  14. class Person { constructor(firstName, lastName) { this.firstName = firstName; this.lastName

    = lastName; } } Object.defineProperty(Person.prototype, 'fullName', { enumerable: true, get() { return `${this.firstName} ${this.lastName}`; } }); const p = new Person('Jeremy', 'Fairbank'); for (const key in p) { console.log(`${key}: ${p[key]}`); } // firstName: Jeremy // lastName: Fairbank // fullName: Jeremy Fairbank
  15. class Person { constructor(firstName, lastName) { this.firstName = firstName; this.lastName

    = lastName; } @enumerable get fullName() { return `${this.firstName} ${this.lastName}`; } } const p = new Person('Jeremy', 'Fairbank'); for (const key in p) { console.log(`${key}: ${p[key]}`); } // firstName: Jeremy // lastName: Fairbank // fullName: Jeremy Fairbank
  16. class Person { constructor(firstName, lastName) { this.firstName = firstName; this.lastName

    = lastName; } @enumerable get fullName() { return `${this.firstName} ${this.lastName}`; } } const p = new Person('Jeremy', 'Fairbank'); for (const key in p) { console.log(`${key}: ${p[key]}`); } // firstName: Jeremy // lastName: Fairbank // fullName: Jeremy Fairbank
  17. class Person { constructor(firstName, lastName) { this.firstName = firstName; this.lastName

    = lastName; } @enumerable get fullName() { return `${this.firstName} ${this.lastName}`; } } const p = new Person('Jeremy', 'Fairbank'); for (const key in p) { console.log(`${key}: ${p[key]}`); } // firstName: Jeremy // lastName: Fairbank // fullName: Jeremy Fairbank
  18. function enumerable(target, name, descriptor) { descriptor.enumerable = true; return descriptor;

    } Object being modified (e.g. prototype or a class based on usage)
  19. function enumerable(target, name, descriptor) { descriptor.enumerable = true; return descriptor;

    } Object literal defining the property that will be added to target
  20. function enumerable(target, name, descriptor) { descriptor.enumerable = true; return descriptor;

    } Optional if you mutate descriptor. Useful if you want to create new descriptor.
  21. Transpiling @enumerable get fullName() { return `${this.firstName} ${this.lastName}`; } let

    descriptor = { get() { return `${this.firstName} ${this.lastName}`; }, enumerable: false, configurable: true }; descriptor = enumerable(Person.prototype, 'fullName', descriptor) || descriptor; Object.defineProperty(Person.prototype, 'fullName', descriptor);
  22. class Person { constructor(name) { this.name = name; } @alias('sayHello')

    greet() { console.log(`Hello. My name is ${this.name}.`); } } const person = new Person('Jeremy'); person.greet(); // Hello. My name is Jeremy. person.sayHello(); // Hello. My name is Jeremy.
  23. function alias(aliasName) { return (target, name, descriptor) => { const

    newDescriptor = Object.assign({}, descriptor, { value(...args) { return this[name](...args); } }); Object.defineProperty( target, aliasName, newDescriptor ); }; }
  24. const Quadrupedal = { walk() { console.log(`${this.name} walks with four

    legs`); } }; @mixin(Quadrupedal, EventEmitter.prototype) class Dog { constructor(name, age) { this.name = name; this.age = age; } haveBirthday() { this.age++; this.emit('birthday', this); } }
  25. const Quadrupedal = { walk() { console.log(`${this.name} walks with four

    legs`); } }; @mixin(Quadrupedal, EventEmitter.prototype) class Dog { constructor(name, age) { this.name = name; this.age = age; } haveBirthday() { this.age++; this.emit('birthday', this); } }
  26. const dog = new Dog('Tucker', 8); dog.on('birthday', d => {

    console.log(`${d.name} just turned ${d.age}`); }); dog.haveBirthday(); // Tucker just turned 9 dog.walk(); // Tucker walks with four legs
  27. const dog = new Dog('Tucker', 8); dog.on('birthday', d => {

    console.log(`${d.name} just turned ${d.age}`); }); dog.haveBirthday(); // Tucker just turned 9 dog.walk(); // Tucker walks with four legs From EventEmitter From Quadrupedal
  28. Why Class Decorators? • Extremely powerful and expressive syntax for

    class/property modification. • Mixins — avoid fragile base class or multiple inheritance issues. • @autobind, @deprecate, @memoize, @readonly, and more! • github.com/jayphelps/core-decorators.js
  29. Object Rest and Spread • Gives objects rest and spread

    capabilities like arrays in ES6. • Use ... operator. • Rest operation is like pick function in lodash or a clone function. • Spread operation is like syntactical sugar for Object.assign. • github.com/sebmarkbage/ecmascript-rest-spread
  30. Rest - Destructuring import isEqual from 'lodash/lang/isEqual'; const assert =

    ::console.assert; const obj = { a: 42, b: 'foo', c: 5, d: 'bar' }; const { a, b, ...rest } = obj; assert(a === 42); assert(b === 'foo'); assert(isEqual(rest, { c: 5, d: 'bar' }));
  31. Rest - Destructuring import isEqual from 'lodash/lang/isEqual'; const assert =

    ::console.assert; const obj = { a: 42, b: 'foo', c: 5, d: 'bar' }; const { a, b, ...rest } = obj; assert(a === 42); assert(b === 'foo'); assert(isEqual(rest, { c: 5, d: 'bar' }));
  32. Spread const obj = { x: 42, y: 100 };

    const newObj = { z: 5, ...obj }; const otherObj = { ...obj, x: 13 }; assert(isEqual(newObj, { x: 42, y: 100, z: 5 })); assert(isEqual(otherObj, { x: 13, y: 100 }));
  33. const obj = { x: 42, y: 100 }; const

    newObj = { z: 5, ...obj }; const otherObj = { ...obj, x: 13 }; assert(isEqual(newObj, { x: 42, y: 100, z: 5 })); assert(isEqual(otherObj, { x: 13, y: 100 })); Spread
  34. const obj = { x: 42, y: 100 }; const

    newObj = { z: 5, ...obj }; const otherObj = { ...obj, x: 13 }; assert(isEqual(newObj, { x: 42, y: 100, z: 5 })); assert(isEqual(otherObj, { x: 13, y: 100 })); Spread
  35. const obj = { x: 42, y: 100 }; const

    newObj = { z: 5, ...obj }; const otherObj = { ...obj, x: 13 }; assert(isEqual(newObj, { x: 42, y: 100, z: 5 })); assert(isEqual(otherObj, { x: 13, y: 100 })); Spread
  36. Transpiling Spread const newObj = { z: 5, ...obj };

    const newObj = Object.assign({ z: 5 }, obj); const otherObj = { ...obj, x: 13 }; const otherObj = Object.assign({}, obj, { x: 13 } );
  37. Usage: Default Options function createDog(options) { const defaults = {

    breed: 'Sheltie', name: 'Tucker' }; return { ...defaults, ...options }; }
  38. Usage: Default Options createDog(); // { breed: 'Sheltie', name: 'Tucker'

    } createDog({ breed: 'Golden Retriever', name: 'Shadow' }); createDog({ name: 'Chance' }); // { breed: 'Sheltie', name: 'Chance' }
  39. Usage: Redux const INITIAL_CONTACT = { name: '', email: ''

    }; function reducer(state = INITIAL_CONTACT, action) { switch(action.type) { case 'UPDATE_NAME': return { ...state, name: action.payload }; case 'UPDATE_EMAIL': return { ...state, email: action.payload }; default: return state; } }
  40. Why Object Rest/Spread? • Succinct syntax instead of Object.assign calls.

    • Pick/omit object properties. • Clone objects. (WARNING: Not a deep clone.) • Provide overridable default options in a function.
  41. Async Functions • Provide synchronous-like syntax for asynchronous code. •

    Prepend function with async. • Use await operator on a promise to obtain a fulfilled promise value. • github.com/tc39/ecmascript-asyncawait
  42. function fetchCustomerNameForOrder(orderId, done, fail) { fetchOrder(orderId, function(err, order) { if

    (err) { logError(err); fail(err); } else { fetchCustomer( order.customerId, function(err, customer) { if (err) { logError(err); fail(err); } else { done(customer.name); } } ) } }) }
  43. async function fetchCustomerNameForOrder(orderId) { let order, customer; try { order

    = await fetchOrder(orderId); } catch(err) { logError(err); throw err; } try { customer = await fetchCustomer(order.customerId); return customer.name; } catch(err) { logError(err); throw err; } }
  44. async function printOrder(orderId) { const order = await fetchOrder(orderId); console.log(order);

    } printOrder(1); Await a promise and obtain the fulfilled/ resolved value. Nonblocking.
  45. Invoke function. async function printOrder(orderId) { const order = await

    fetchOrder(orderId); console.log(order); } printOrder(1);
  46. Wrap awaited expression in Promise. async function printOrder(orderId) { const

    promise = Promise.resolve( fetchOrder(orderId) ); console.log(order); } printOrder(1);
  47. Wrap remaining code in a then callback. async function printOrder(orderId)

    { const promise = Promise.resolve( fetchOrder(orderId) ); console.log(order); } printOrder(1);
  48. function printOrder(orderId) { const promise = Promise.resolve( fetchOrder(orderId) ); return

    promise.then(order => { console.log(order); return Promise.resolve(undefined); }); } printOrder(1); Wrap remaining code in a then callback.
  49. No explicit return, so return a resolved undefined value. function

    printOrder(orderId) { const promise = Promise.resolve( fetchOrder(orderId) ); return promise.then(order => { console.log(order); return Promise.resolve(undefined); }); } printOrder(1);
  50. function printOrder(orderId) { fetchOrder(orderId) .then(order => console.log(order)) .catch(e => console.log('error

    retrieving order', e)); } async function printOrder(orderId) { try { const order = await fetchOrder(orderId); console.log(order); } catch(e) { console.log('error retrieving order', e); } }
  51. function printOrder(orderId) { fetchOrder(orderId) .then(order => console.log(order)) .catch(e => console.log('error

    retrieving order', e)); } async function printOrder(orderId) { try { const order = await fetchOrder(orderId); console.log(order); } catch(e) { console.log('error retrieving order', e); } }
  52. function printOrder(orderId) { fetchOrder(orderId) .then(order => console.log(order)) .catch(e => console.log('error

    retrieving order', e)); } async function printOrder(orderId) { try { const order = await fetchOrder(orderId); console.log(order); } catch(e) { console.log('error retrieving order', e); } }
  53. function printOrder(orderId) { fetchOrder(orderId) .then(order => console.log(order)) .catch(e => console.log('error

    retrieving order', e)); } async function printOrder(orderId) { try { const order = await fetchOrder(orderId); console.log(order); } catch(e) { console.log('error retrieving order', e); } }
  54. Sequential async function printOrders(orderIds) { const orders = []; for

    (const id of orderIds) { const order = await fetchOrder(id); orders.push(order); } console.log(orders); } printOrders([1, 2, 3]);
  55. Sequential async function printOrders(orderIds) { const orders = []; for

    (const id of orderIds) { const order = await fetchOrder(id); orders.push(order); } console.log(orders); } printOrders([1, 2, 3]); Each step of loop has to wait. Issue if requests aren’t dependent on each other.
  56. Parallel async function printOrders(orderIds) { const orders = await Promise.all(

    orderIds.map(id => fetchOrder(id)) ); console.log(orders); } printOrders([1, 2, 3]);
  57. Parallel async function printOrders(orderIds) { const orders = await Promise.all(

    orderIds.map(id => fetchOrder(id)) ); console.log(orders); } printOrders([1, 2, 3]); Make all requests at once and resolve with Promise.all. Allows for “parallelism.”
  58. Why Async Functions? • Synchronous-looking asynchronous code. • Gain back

    use of native language flow control constructs with asynchronous code (e.g. for..of, try/catch). • Asynchronous coroutines.
  59. Other upcoming features • Stage 3: SIMD, Exponentiation operator, Array.prototype.includes

    • Stage 2: Object.observe • Stage 1: Typed objects, class property initializers, shared memory and atomics, Observable • Stage 0: function bind operator, do expressions • Many more!
  60. Resources • https://github.com/tc39/ecma262 • http://babeljs.io/ • $ babel --stage 0

    myAwesomeES7code.js • https://tc39.github.io/process-document/ • https://esdiscuss.org/ • http://www.ecma-international.org/memento/ contribute_TC39_Royalty_Free_Task_Group.php