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

HTML5DevConf ES7 and Beyond!

HTML5DevConf ES7 and Beyond!

Avatar for Jeremy Fairbank

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