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

JS Summit: ES7 and Beyond!

JS Summit: ES7 and Beyond!

Jeremy Fairbank

February 24, 2016
Tweet

More Decks by Jeremy Fairbank

Other Decks in Programming

Transcript

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

    to a standard feature. • https://tc39.github.io/process-document
  2. 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 }
  3. Stage 1 - Proposal • Make a case for addition

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

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

    refinements. • SIMD - Single instruction, multiple data • Async Functions
 
 async function loadOrders() { const orders = await fetchJson('/orders'); console.log(orders); }
  6. Stage 4 - Finished • Ready to be included in

    the next ES standard. • Exponentiation operator
 • Array.prototype.includes 3 ** 2; // 9 [1, 2, 3].includes(2); // true
  7. Class Decorators • Annotations. • @expression that evaluates to a

    function. • Precedes a class or property. • github.com/wycats/javascript-decorators
 

  8. 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
  9. 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.
  10. 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
  11. 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
  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; } @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
  14. 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
  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. function enumerable(target, name, descriptor) { descriptor.enumerable = true; return descriptor;

    } Object being modified (e.g. prototype or a class based on usage)
  17. { get() { return `${this.firstName} ${this.lastName}`; }, set: undefined, enumerable:

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

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

    Object literal defining the property that will be added to target { type: 'accessor', hint: 'getter', property: { name: () => 'fullName', initializer: () => ({ get() { return `${this.firstName} ${this.lastName}`; } }), get(obj) { return obj.fullName; }, set(obj, value) { obj.fullName = value; } }, enumerable: false, configurable: true }
  20. function enumerable(target, descriptor) { descriptor.enumerable = true; return descriptor; }

    Optional if you mutate descriptor. Useful if you want to create new descriptor.
  21. let descriptor = { property: { name: () => 'fullName',

    initializer: () => ({ get() { return `${this.firstName} ${this.lastName}`; } }), get(...) { ... } set(...) { ... } }, enumerable: false, configurable: true }; descriptor = enumerable(Person.prototype, descriptor) || descriptor; Object.defineProperty(Person.prototype, descriptor); @enumerable get fullName() { return `${this.firstName} ${this.lastName}`; } ≈
  22. let descriptor = { property: { name: () => 'fullName',

    initializer: () => ({ get() { return `${this.firstName} ${this.lastName}`; } }), get(...) { ... } set(...) { ... } }, enumerable: false, configurable: true }; descriptor = enumerable(Person.prototype, descriptor) || descriptor; Object.defineProperty(Person.prototype, descriptor); @enumerable get fullName() { return `${this.firstName} ${this.lastName}`; } ≈
  23. 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.
  24. function alias(aliasName) { return (target, descriptor) => { const name

    = descriptor.property.name(); const newDescriptor = Object.assign({}, descriptor, { value(...args) { return this[name](...args); } }); Object.defineProperty( target, aliasName, newDescriptor ); }; }
  25. function alias(aliasName) { return (target, descriptor) => { const name

    = descriptor.property.name(); const newDescriptor = Object.assign({}, descriptor, { value(...args) { return this[name](...args); } }); Object.defineProperty( target, aliasName, newDescriptor ); }; }
  26. function alias(aliasName) { return (target, descriptor) => { const name

    = descriptor.property.name(); const newDescriptor = Object.assign({}, descriptor, { value(...args) { return this[name](...args); } }); Object.defineProperty( target, aliasName, newDescriptor ); }; }
  27. 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); } }
  28. 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); } }
  29. 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); } }
  30. 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
  31. 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
  32. 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
  33. Object Rest and Spread • Rest and spread capabilities like

    arrays in ES6. • Use ... operator. • Rest operation is like pick function in lodash. • Spread is like a clone function. • Syntactical sugar for Object.assign. • github.com/sebmarkbage/ecmascript-rest-spread

  34. 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' }));
  35. 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' }));
  36. 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 }));
  37. 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
  38. 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
  39. 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
  40. 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 } );
  41. Usage: Default Options function createDog(options) { const defaults = {

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

    } createDog({ breed: 'Golden Retriever', name: 'Shadow' }); createDog({ name: 'Chance' }); // { breed: 'Sheltie', name: 'Chance' }
  43. 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; } }
  44. 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.
  45. 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
  46. 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); } } ) } }) }
  47. 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; } }
  48. async function printOrder(orderId) { const order = await fetchOrder(orderId); console.log(order);

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

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

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

    { const promise = Promise.resolve( fetchOrder(orderId) ); console.log(order); } printOrder(1);
  52. 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.
  53. 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);
  54. 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); } }
  55. 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); } }
  56. 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); } }
  57. 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); } }
  58. 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]);
  59. 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.
  60. Parallel async function printOrders(orderIds) { const orders = await Promise.all(

    orderIds.map(id => fetchOrder(id)) ); console.log(orders); } printOrders([1, 2, 3]);
  61. 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.”
  62. 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.
  63. Other notable features • Stage 4: Exponentiation operator, Array.prototype.includes •

    Stage 3: SIMD • Stage 2: Shared memory and atomics • Stage 1: Class property initializers, callable class constructors, Observable • Stage 0: function bind operator, do expressions, function decorators, method parameter decorators • Many more!
  64. Resources • https://github.com/tc39/ecma262 • https://tc39.github.io/ecma262/ • http://babeljs.io/ • https://tc39.github.io/process-document/ •

    https://esdiscuss.org/ • http://www.ecma-international.org/memento/ contribute_TC39_Royalty_Free_Task_Group.php