ES6+

O presente e o futuro do Javascript

Rodrigo Araújo @dygufa

Victor Magalhães
@vhfmag

JS?

ES5?

ESNext?

Versões da ECMA-262

  • ES1 - 1997
  • ES2 - 1998
  • ES3 - 1999
  • ES4 - gongada pela Microsoft
  • ES5 - 2009
  • ES6 - Junho de 2015
  • ES7 - Junho de 2016
  • ES8 - Junho de 2017
  • ES9 - coming soon!

TC39

Comitê responsável pela especificação

o processo é aberto e participativo!

http://tc39.github.io/

Para ver todas as features

ES6

O renascimento

let e const

Arrow function

spread 

Destructuring

For of

Default parameter

Map/Set/WeakMap/WeakSet

Classes

Promises

Generators

Template literals

var a = "lindo";
var b = "bbk";
console.log(`Victor é ${a} and
também é ${b}.`);

Trailing comma

const palavrasApenas = [
    "almofada",
    "chafariz",
    "salamanda",
];

const palavrasApenasComNumeros = [
    "almofada": 1,
    "chafariz": 2,
    "salamanda": 3,
];

Array: from, of, find, findIndex, fill

Object: assign

ES7/ES2016

Operador de exponenciação

const byte = 2 ** 8;

Array.includes

const pessoasPresentes = ["Josefina", "Mário Alberto"];
pessoasPresentes.includes("Josefina"); // true
pessoasPresentes.includes("Stephanie"); // false

ES8/ES2017

mostly async, tho

Async/Await

async function getOrgs(user) {
    const url =
        `https://api.github.com/users/${user}/orgs`;
    const orgs = await fetch(url).then(res => res.json());
    return orgs;
}

Object: values e entries

const peopleById = {
    dygufa: "Rodrigo Araújo",
    vhfmag: "Victor Magalhães",
};

const ids = Object.keys(peopleById);
const names = Object.values(peopleById);
for (const [id, name] of Object.entries(peopleById) {
    console.log(`id: ${id}, nome: ${name}`);
}

String: padStart e padEnd

function formatDate(date) {
    const month = date.getMonth() + 1;
    const date = date.getDate();
    const year = date.getFullYear();

    return [date, month, year]
        .map(x => x.toString().padStart(2, "0"))
        .join("/");
}

formatDate(new Date); // "10/05/2018"

ES9/ES2018

Rest/Spread

({a, b, ...rest} = {a: 10, b: 20, c: 30, d: 40});
console.log(a); // 10
console.log(b); // 20
console.log(rest); //{c: 30, d: 40}

Iteradores Assíncronos

import React from "react";
import { render } from "react-dom";

const sleep = (n) => new Promise(res => setTimeout(res, n));

async function* ping(n) {
  let count = 0;
  while (true) {
    await sleep(n);
    yield ++count;
  }
}

class App extends React.Component {
  state = { seconds: 0 };

  async componentDidMount() {
    for await (const seconds of ping(1000)) this.setState({ seconds });
  }

  render() {
    return (
      <div>
        <img src="https://media1.giphy.com/media/qQx7B5z3hG19K/giphy.gif" />
        <h2>You have been rickrolled for {this.state.seconds} seconds</h2>
      </div>
    );
  }
}

render(<App />, document.getElementById("root"));

Promise.finally

fetch("some.api/endpoint")
    .then(res => res.json())
    .catch(() => new Error("oh shit"))
    .finally(() => model.cleanStuff())

Regex named captured groups

const dateRegexp = /(?<date>\d+)\/(?<month>\d+)\/(?<year>\d+)/;

let {
    groups: { date, year, month }
} = dateRegexp.exec("12/01/2018")

Futuro (maybe?)

stage-3
-

candidate

Array: flatMap & flatten

const pessoas = [
    { name: "Victor", grades: [8, 5, 8] },
    { name: "Dygufa", grades: [9, 7, 6] },
    { name: "Clara", grades: [8.5, 5, 9] },
];

const todasAsNotas = pessoas
    .flatMap(({ grades }) => grades);
const todasAsNotas2 = pessoas
    .map(({ grades }) => grades)
    .flatten();

String: trimStart & trimEnd

const pessoas = [
    { name: "Victor", grades: [8, 5, 8] },
    { name: "Dygufa", grades: [9, 7, 6] },
    { name: "Clara", grades: [8.5, 5, 9] },
];

const todasAsNotas = pessoas
    .flatMap(({ grades }) => grades);
const todasAsNotas2 = pessoas
    .map(({ grades }) => grades)
    .flatten();

Campos públicos e privados em classes

class Store {
    #user = null;
    lastUpdated = new Date();
    
    async fetchUser() {
        const res = await fetch(/**/);
        this.#user = await res.json();
        this.#onUpdate();
    }
    
    #onUpdate() {
        this.lastUpdated = new Date();
    }
}

import() - import dinâmico

import { flatMap } from "lodash";

async function getAllCells(src) {
    if (src instanceof File) {
        const XLSX = await import("xlsx");
        return flatMap(XLSX.fromFile(src).sheets[0]);
    } else {
        return flatMap(src, x => x);
    }
}

stage-2
-

draft

decoradores

@autobind
class Store {
    @observable user = null;
    
    async fetchUser(id) {
        return await fetch(/**/)
            .then(res => res.json());
    }

    @action clearUser() {
        this.user = null;
    }

    @computed userId() {
        return this.user && this.user.id;
    }
}

campos estáticos públicos e privados

class Utils {
    static #parseDate(raw) {
        return moment(raw).toDate();
    }

    static parseUser(raw) {
        return { ...raw, birthday: Utils.#parseDate(raw) };
    }
}

throw como expressão

function getEncoder(encoding = throw new Error("Argument required")) {
  return encoding === "utf8" ? new UTF8Encoder() 
    : encoding === "utf16le" ? new UTF16Encoder(false) 
    : encoding === "utf16be" ? new UTF16Encoder(true) 
    : throw new Error("Unsupported encoding");
}

stage-1
-

proposal

observables

function listen(element, eventName) {
    return new Observable(observer => {
        // Create an event handler which sends data to the sink
        let handler = event => observer.next(event);

        // Attach the event handler
        element.addEventListener(eventName, handler, true);

        // Return a cleanup function which will cancel the event stream
        return () => {
            // Detach the event handler from the element
            element.removeEventListener(eventName, handler, true);
        };
    });
}
function commandKeys(element) {
    let keyCommands = { "38": "up", "40": "down" };

    return listen(element, "keydown")
        .filter(event => event.keyCode in keyCommands)
        .map(event => keyCommands[event.keyCode])
}

let subscription = commandKeys(inputElement).subscribe({
    next(val) { console.log("Received key command: " + val) },
    error(err) { console.log("Received an error: " + err) },
    complete() { console.log("Stream complete") },
});

temporal

new CivilDate(year, month, day);
new CivilTime(hour, minute[[[, second], millisecond], nanosecond]);
new CivilDateTime(year, month, day, hour, minute, [...]);

new Instant(milliseconds[, nanoseconds]);
new ZonedInstant(instant, timeZone);

operador pipeline

const doubleSay = (str) => str + ", " + str;

const capitalize = ([first, ...rest]) =>
    first.toUpperCase() + rest.join("");

const exclaim = (str) => str + '!';

let result1 = exclaim(capitalize(doubleSay("hello")));

let result2 = "hello"
  |> doubleSay
  |> capitalize
  |> exclaim;

result1 // "Hello, hello!"
result1 === result2 // true

partial application

let newScore = player.score
  |> somar(7, ?)
  |> limitar(0, 100, ?);

const maxGreaterThanZero = Math.max(0, ...);
maxGreaterThanZero(1, 2); // 2
maxGreaterThanZero(-1, -2); // 0

nullish coalescing e optional chaining

async function makeRequest(options) {
    const maxCacheTime = options?.maxCacheTime ?? 1E4;
    const skipHandshake = options?.skipHandshake ?? true;
    const userName = options?.userName ?? "@anonymous";
    // do stuff
}

do expression

return (
  <nav>
    <Home />
    {
      do {
        if (loggedIn) {
          <LogoutButton />
        } else {
          <LoginButton />
        }
      }
    }
  </nav>
)

slice notation

const grades = [9, 8, 10, 5, 7];
const someGrades = grades[2:4];
const tail = grades[1:];
const evens = grades[::2];
const odds = grades[1::2];
const reverse = grades[::-1];

stage-0
-

strawman

pattern matching

const res = await fetch(jsonService)
const val = match (res) {
  {status: 200, headers: {'Content-Length': s}} => `size is ${s}`,
  {status: 404} => 'JSON not found',
  {status} if (status >= 400) => throw new RequestError(res)
}

return (
    <Fetch url={API_URL}>{
      props => match (props) {
        {loading} if (loading) => <Loading />,
        {error} => <Error error={error} />,
        {data} => <Page data={data} />
      }
    }</Fetch>
);

obrigado!

ES6+

By Victor Magalhães

ES6+

Um panorama das principais features das versões mais recentes do EcmaScript!

  • 611