ES6 / ES2015 Gotchas

William Grasel

@willgmbr

Let + Const

  • Escopo fechado sempre
  • Restrinções estáticas
  • Throw Erros
  • Constantes não são imutaveis!
  • De preferencia a const sempre que possível
  • Evite var, ao menos que realmente saiba o que esta fazendo e por que precisa disso!

Escopo fechado sempre... MESMO


 "use strict";
 var methods = [];

 for(var i = 0; i < 10; i++){
   methods[i] = function() {
     console.log('var = ', i);
   };
 }

 methods[5]() // var = 10

 "use strict";
 var methods = [];

 for(let i = 0; i < 10; i++){
   methods[i] = function() {
     console.log('let = ', i);
   };
 }

 methods[5]() // let = 5

!==

Constantes podem ter valor atribuído apenas uma vez


 "use strict";

 const c = {};

 c.myNewAtt = 123;
 console.log('myconst = ', c);
 //myconst = { myNewAtt: 123 }

 c = 123;
 //TypeError: Assignment to constant variable.

Mas não são imutáveis...

Promises

  • Representam uma operação única
  • Podem ser assíncronas ou não...
  • Uma maneira de lidar com callback hell

Evitando callback hell:


  remotedb.allDocs({  
    include_docs: true,
    attachments: true
  }).then(function (result) {
    var docs = result.rows;
    docs.forEach(function(element) {
      localdb.put(element.doc).then(function(response) {
        console.log("Pulled doc with id ",  element.doc._id);
      }).catch(function (err) {
        if (err.status == 409) {
          localdb.get(element.doc._id).then(function (resp) {
            localdb.remove(resp._id, resp._rev).then(function (resp) {
              // VOCÊ ESTA FAZENDO ISSO ERRADOOOOOOO!!!!!

Que tal?


  remotedb.allDocs(...)  
  .then( resultOfAllDocs => {
    return localdb.put(...);
  })
  .then( resultOfPut => {
    return localdb.get(...);
  })
  .then( resultOfGet => {
    return localdb.put(...);
  })
  .catch( err => {
    console.log(err);
  });

Pode não ser assíncrono...


  facaAlgo().then(function() {  
    facaOutraCoisa();
  })
  .then(function(arg) {
    console.log(arg); // undefined / sync
    return 'VALOR';
  })
  .then(function(arg) {
    console.log(arg); // 'VALOR' / sync
    return Promise.resolve('MYPROMISE');
  })
  .then(function(arg) {
    console.log(arg); // 'MYPROMISE' / async
  });

Lidando com multiplas promises


  db.allDocs({include_docs: true}).then( result => {  
    result.rows.forEach( row => {
      db.remove(row.doc);  
    });
  }).then(() => {
    // Será que funciona??
  });

  db.allDocs({include_docs: true}).then( result => {  
    return Promise.all(result.rows.map( row => {
      return db.remove(row.doc);
    }));
  }).then( arrayDeResultados => {
    // Agora funciona!
  });

Catching errors


  myPromise()
  .then(() => makePromise())
  .then(() => makePromise())
  .then(() => makePromise())
  .catch(error => {
    window.location.href = 
      "http://stackoverflow.com/search" + error.message;
  });

Arrow Functions

  • Não é apenas sugar sintaxe!
  • Compartilham o contexto de onde ela é criada
  • Não criam seu próprio 'this' and 'arguments'
  • Não podem ser usadas para criação de objetos
  • Não tem seu próprio prototype
  • Implementação otimizada e mais leve

Elas não tem seu próprio contexto... MESMO!


 let foo = {
   x: 666,
   normal: function(){ console.log(this.x, arguments) },
   arrow: () => console.log(this.x, arguments)
 }

 let foo = {
   x: 666,
   normal: function(){ console.log(this.x, arguments) },
   arrow: () => console.log(this.x, arguments)
 }

 foo.normal(1, 2)
 foo.normal.bind({x: 69})(2, 1)
 foo.normal.call({x: 69}, 2, 1)

 let foo = {
   x: 666,
   normal: function(){ console.log(this.x, arguments) },
   arrow: () => console.log(this.x, arguments)
 }

 foo.normal(1, 2) // 666, { '0': 1, '1': 2 }
 foo.normal.bind({x: 69})(2, 1) // 69, { '0': 2, '1': 1 }
 foo.normal.call({x: 69}, 2, 1) // 69, { '0': 2, '1': 1 }

 let foo = {
   x: 666,
   normal: function(){ console.log(this.x, arguments) },
   arrow: () => console.log(this.x, arguments)
 }

 foo.normal(1, 2) // 666, { '0': 1, '1': 2 }
 foo.normal.bind({x: 69})(2, 1) // 69, { '0': 2, '1': 1 }
 foo.normal.call({x: 69}, 2, 1) // 69, { '0': 2, '1': 1 }

 foo.arrow(1, 2)
 foo.arrow.bind({x: 69})(2, 1)
 foo.arrow.call({x: 69}, 2, 1)

 let foo = {
   x: 666,
   normal: function(){ console.log(this.x, arguments) },
   arrow: () => console.log(this.x, arguments)
 }

 foo.normal(1, 2) // 666, { '0': 1, '1': 2 }
 foo.normal.bind({x: 69})(2, 1) // 69, { '0': 2, '1': 1 }
 foo.normal.call({x: 69}, 2, 1) // 69, { '0': 2, '1': 1 }

 foo.arrow(1, 2) // undefined, undefined
 foo.arrow.bind({x: 69})(2, 1) // undefined, undefined
 foo.arrow.call({x: 69}, 2, 1) // undefined, undefined

 let foo = {
   x: 666,
   normal: function(){ console.log(this.x, arguments) },
   arrow: () => console.log(this.x, arguments)
 }

 foo.normal(1, 2) // 666, { '0': 1, '1': 2 }
 foo.normal.bind({x: 69})(2, 1) // 69, { '0': 2, '1': 1 }
 foo.normal.call({x: 69}, 2, 1) // 69, { '0': 2, '1': 1 }

 foo.arrow(1, 2) // undefined, undefined
 foo.arrow.bind({x: 69})(2, 1) // undefined, undefined
 foo.arrow.call({x: 69}, 2, 1) // undefined, undefined

 //BONUS:
 foo.normal.bind({x:69}).bind({x:999})(123) // ???

Não são construtores e não tem protótipo...


 let foo = {
   x: 666,
   normal: function(){ console.log(this.x, arguments) },
   arrow: () => console.log(this.x, arguments)
 }

 let foo = {
   x: 666,
   normal: function(){ console.log(this.x, arguments) },
   arrow: () => console.log(this.x, arguments)
 }

 console.log(foo.normal.prototype); // {}

 console.log(new foo.normal); // {}

 let foo = {
   x: 666,
   normal: function(){ console.log(this.x, arguments) },
   arrow: () => console.log(this.x, arguments)
 }

 console.log(foo.normal.prototype); // {}

 console.log(new foo.normal); // {}

 console.log(foo.arrow.prototype); // undefined

 console.log(new foo.arrow);
 // TypeError: () => console.log(this.x, arguments) is not a constructor

JavaScript Modules

  • Módulos nativos a nível de linguagem
  • Análise estática de estrutura
  • Importes parciais
  • Modelo de carregamento assíncrono

Análise estática?


 // lib/math.js

 export function sum(x, y) {
   return x + y;
 }

 export default 3.141593;

 // app.js

 import pi, {sum} from "lib/math";

 console.log("2π = " + sum(pi, pi));

 if(true) {
   import * as name from "anything";
   // Throw Error
 }

Tudo é carregado antes da execução, de forma assíncrona!

Estruturas de Dados Eficientes

  • Sets e Maps iteráveis
  • Implementação otimizada e mais leve
  • Maps aceitam chaves de qualquer tipo
  • WeakSet e WeakMap não iteráveis
  • Opção sem vazamento e retenção de dados
  • Melhores estratégias para lidar com o  Garbage Collection

Estruturas Iteráveis


 const mySet = new Set();

 mySet
   .add("hello")
   .add("hello")
   .add(123);

 console.log(mySet.size);
 // 2

 console.log(mySet.has("hello"));
 // true

 for(i of mySet)
   console.log(i);
   // hello
   // 123

 const myMap = new Map();

 myMap
   .set("hello", 42)
   .set("hello", 34)
   .set(123, "value");

 console.log(myMap.size);
 // 2

 console.log(myMap.get("hello"));
 // 34

 for(i of myMap)
   console.log(i);
   // [ 'hello', 34 ]
   // [ 123, 'value' ]

Estruturas não Iteráveis Sem Vazamento


 const myWSet = new WeakSet(),
     myObj = { abc: 123 };

 myWSet
   .add(myObj)
   .add(myObj)
   .add({});

 console.log(myWSet.size);
 // undefined

 for(i of myWSet)
   console.log(i);
   // Throw Error

 console.log(myWSet.has(myObj));
 // true

 const myWMap = new WeakMap(),
       myObj = { abc: 123 };

 myWMap
   .set(myObj, 42)
   .set(myObj, 34)
   .set({}, "value");

 console.log(myWMap.size);
 // undefined

 for(i of myWMap)
   console.log(i);
   // Throw Error

 console.log(myWMap.get(myObj));
 // 34

Generators

  • São funções que podem ser pausadas
  • Podem retornar múltiplos valores
  • Um retorno para cada pausa
  • Podem retornar valores infinitamente
  • Sugar sintaxe para a nova interface de interators

Exemplo simples


  function* sayHello() {  
    yield "Ola";
    yield "mundo!";
  }

  for(let i of sayHello())  
    console.log(i);
  // "Ola"
  // "mundo!"

Por baixo dos panos:


  function* sayHello() {  
    yield "Ola";
    yield "mundo!";
  }

  const iterator = contar(3);
  // { next() }

  iterator.next()
  // { value: "Ola", done: false }

  iterator.next()
  //{ value: "mundo!", done: true }

Symbols

  • Novo tipo primitivo do Javascript
  • São sempre, sempre únicos!
  • Podem conter uma descrição opicional
  • Podem ser usadas como chave de acesso a objetos
  • Permitem acesso a propriedades quaaase privados
  • Mas pode ser acessados com  'Object.getOwnPropertySymbols'
  • Principal objetivo deles é evoluir a linguagem evitando breaking changes!

Symbols são sempre únicos, MESMO!


  Symbol("key") === Symbol("key") // FALSE

  Symbol("key") === Symbol.for("key") //FALSE

  Symbol.for("key") === Symbol.for("key") // TRUE

  console.log(Symbol("key")) // Symbol(key)

Symbols permitem propriedades privadas... ou quase!


  const privateProp = Symbol("myPrivate");

  const obj = {
    ["normalProp"]: "normalProp",
    [privateProp]: "symbolProp"
  }

  console.log(Object.keys(obj));
  // [ 'normalProp' ]

  console.log(obj[privateProp]);
  // 'symbolProp'

  console.log(Object.getOwnPropertySymbols(obj));
  // [ Symbol(myPrivate) ]

Built-in Symbols para novas funcionalidades do JavaScrip


  Symbol.iterator

  Symbol.match

  Symbol.species

  const obj = {
    [Symbol.iterator]: function*(){
      yield 1;
      yield 2;
      yield 3;
    }
  }

  for(const i of obj) console.log(i);
  // 1
  // 2
  // 3

Classes

  • Apenas sugar sintaxe para OO do JS baseada em Prototypes
  • Não acrecenta nada de novo que já não era possível antes
  • Permite construção de objetos de maneira mais clara e legivel

  • OO clássica baseada em classes pode induzir a más práticas

  • Grandes poderes vem com grandes responsabilidades

  • Lembre-se sempre: use composição ao invez de herança

Exemplo


  class MyArray extends Array {
    constructor() {
      super(arguments);
      this.myInstanceProp = "hey";
    }
    toString() {
      return "My Own " + super.toString();
    }
    static defaultArray() {
      return new MyArray(6, 6, 6);
    }
  }

  const my = MyArray.defaultArray();
  console.log(my.toString());
  //My Own [object Arguments]

Perguntas??

That's all folks!

Obrigado! =)

ECMAScript 6 Gotchas

By William Grasel

ECMAScript 6 Gotchas

Descrições gerais das features do ES6/ES2015 podem ser encontradas em muitos lugares na internet, o foco aqui é falar de alguns pontos bem particulares e algumas pegadinhas que normalmente aprendemos apenas no dia a dia de trabalho, e daquelas que ninguém sabe nem por que existem!

  • 2,199