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. ES7
    and Beyond!
    Jeremy Fairbank
    blog.jeremyfairbank.com
    @elpapapollo
    github.com/jfairbank

    View Slide

  2. We help brands excel.
    simplybuilt.com
    Your website, SimplyBuilt.
    pushagency.io

    View Slide

  3. ES2016
    ES.next
    ES.later
    ES.whatisgoingon?
    ES7
    ¯\_(ϑ)_/¯

    View Slide

  4. Many proposed
    features coming later.

    View Slide

  5. View Slide

  6. ECMA-262
    TC39

    View Slide

  7. TC39 Process
    • Five stage process for converting
    an idea to a standard feature.
    • https://tc39.github.io/process-document

    View Slide

  8. 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 }

    View Slide

  9. 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; }
    }

    View Slide

  10. Stage 2 - Draft
    • Precise syntax and semantics;
    initial spec.
    • Object rest and spread properties
    let obj = { ...otherObj, x: 'foo' };

    View Slide

  11. 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);
    }

    View Slide

  12. 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

    View Slide

  13. View Slide

  14. Class Decorators
    • Annotations.
    • @expression that evaluates to a
    function.
    • Precedes a class or property.
    • github.com/wycats/javascript-decorators


    View Slide

  15. 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

    View Slide

  16. 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.

    View Slide

  17. Solution?

    View Slide

  18. 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

    View Slide

  19. 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

    View Slide

  20. 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

    View Slide

  21. More complex and
    less declarative.

    View Slide

  22. 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

    View Slide

  23. 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

    View Slide

  24. 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

    View Slide

  25. How do decorators
    work?

    View Slide

  26. function enumerable(target, name, descriptor) {
    descriptor.enumerable = true;
    return descriptor;
    }
    Legacy Proposal (Babel 5 Support)

    View Slide

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

    View Slide

  28. function enumerable(target, name, descriptor) {
    descriptor.enumerable = true;
    return descriptor;
    }
    Name of property
    that will be added
    to target

    View Slide

  29. {
    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

    View Slide

  30. Current Proposal (No Babel Support)
    function enumerable(target, descriptor) {
    descriptor.enumerable = true;
    return descriptor;
    }

    View Slide

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

    View Slide

  32. 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
    }

    View Slide

  33. function enumerable(target, descriptor) {
    descriptor.enumerable = true;
    return descriptor;
    }
    Optional if you mutate
    descriptor. Useful if
    you want to create
    new descriptor.

    View Slide

  34. 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}`;
    } ≈

    View Slide

  35. 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}`;
    } ≈

    View Slide

  36. Parameterized
    Decorators

    View Slide

  37. 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.

    View Slide

  38. 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
    );
    };
    }

    View Slide

  39. 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
    );
    };
    }

    View Slide

  40. 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
    );
    };
    }

    View Slide

  41. Decorated Classes

    View Slide

  42. 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);
    }
    }

    View Slide

  43. 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);
    }
    }

    View Slide

  44. 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);
    }
    }

    View Slide

  45. 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

    View Slide

  46. 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

    View Slide

  47. 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

    View Slide

  48. 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


    View Slide

  49. 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' }));

    View Slide

  50. 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' }));

    View Slide

  51. 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 }));

    View Slide

  52. 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

    View Slide

  53. 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

    View Slide

  54. 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

    View Slide

  55. 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 }
    );

    View Slide

  56. Usage: Default Options
    function createDog(options) {
    const defaults = {
    breed: 'Sheltie',
    name: 'Tucker'
    };
    return { ...defaults, ...options };
    }

    View Slide

  57. Usage: Default Options
    createDog();
    // { breed: 'Sheltie', name: 'Tucker' }
    createDog({
    breed: 'Golden Retriever',
    name: 'Shadow'
    });
    createDog({ name: 'Chance' });
    // { breed: 'Sheltie', name: 'Chance' }

    View Slide

  58. 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;
    }
    }

    View Slide

  59. 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.

    View Slide

  60. 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

    View Slide

  61. 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);
    }
    }
    )
    }
    })
    }

    View Slide

  62. function fetchCustomerNameForOrder(orderId) {
    return fetchOrder(orderId)
    .then(order => fetchCustomer(order.customerId))
    .then(customer => customer.name)
    .catch(err => {
    logError(err);
    throw err;
    });
    }

    View Slide

  63. Workarounds

    View Slide

  64. 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;
    }
    }

    View Slide

  65. Synchronous-looking

    View Slide

  66. async function printOrder(orderId) {
    const order = await fetchOrder(orderId);
    console.log(order);
    }
    printOrder(1);

    View Slide

  67. async function printOrder(orderId) {
    const order = await fetchOrder(orderId);
    console.log(order);
    }
    printOrder(1);
    Declare a
    function as
    async

    View Slide

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

    View Slide

  69. async function printOrder(orderId) {
    const order = await fetchOrder(orderId);
    console.log(order);
    }
    printOrder(1);
    x x

    View Slide

  70. How do async
    functions work at a
    high level?

    View Slide

  71. async function printOrder(orderId) {
    const order = await fetchOrder(orderId);
    console.log(order);
    }
    printOrder(1);

    View Slide

  72. Invoke function.
    async function printOrder(orderId) {
    const order = await fetchOrder(orderId);
    console.log(order);
    }
    printOrder(1);

    View Slide

  73. async function printOrder(orderId) {
    const order = await fetchOrder(orderId);
    console.log(order);
    }
    printOrder(1);
    Encounter await.

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  77. 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.

    View Slide

  78. 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);

    View Slide

  79. Error Handling

    View Slide

  80. 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);
    }
    }

    View Slide

  81. 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);
    }
    }

    View Slide

  82. 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);
    }
    }

    View Slide

  83. 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);
    }
    }

    View Slide

  84. Sequential vs. Parallel

    View Slide

  85. 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]);

    View Slide

  86. 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.

    View Slide

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

    View Slide

  88. 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.”

    View Slide

  89. Sequential vs. Parallel
    Demo

    View Slide

  90. Moral: don’t use
    sequential awaits
    unless you need
    serialization!

    View Slide

  91. 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.

    View Slide

  92. 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!

    View Slide

  93. 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


    View Slide

  94. Thanks!
    Code:
    github.com/jfairbank/es7-and-beyond-talk
    Jeremy Fairbank
    blog.jeremyfairbank.com
    @elpapapollo
    github.com/jfairbank

    View Slide