ES6 para humanos
Sumário
Idiomas
1. let, const e escopo de bloco
let permite que você crie declarações limitadas a qualquer bloco, o que é chamado de escopo de bloco. Ao invés de usar var, que fornece escopo de função, é recomendado que se use let no ES6.
var a = 2;
{
let a = 3;
console.log(a); // 3
}
console.log(a); // 2
Outra forma de fazer declaração de escopo de block é usando const, que cria constantes. Em ES6, uma const representa uma referência constante para um valor. Em outras palavras, o valor não está congelado, apenas a atribuição dele. Segue um exemplo.
{
const ARR = [5, 6];
ARR.push(7);
console.log(ARR); // [5,6,7]
ARR = 10; // TypeError
ARR[0] = 3; // valor não é imutável
console.log(ARR); // [3,6,7]
}
Algumas coisas a se lembrar:
- "hoisting" de
let e const variam da forma tradicional de "hoisting" de variáveis e funções. Ambos estão "hoistados", mas não podem ser acessados antes das suas declarações, por causa da Temporal Dead Zone
let e const são escopadas aos fechamentos de bloco mais próximos.
- Quando usar
const, use CAPITAL_CASING
const deve ser definida na sua declaração
2. Arrow Functions
Arrow Functions são uma notação short-hand para escrever funções em ES6. A definição de arrow function consiste de uma lista de parâmetros ( ... ), seguido de um => e o corpo da função.
let adicao = function(a, b) {
return a + b;
};
// Implementação com Arrow Function
let adicao = (a, b) => a + b;
Note que no exemplo acima, a arrow function adicao é implementada com um "corpo conciso" que não precisa de declaração de retorno explícita.
Segue um exemplo com o "corpo de bloco"
let arr = ['maçã', 'banana', 'laranja'];
let cafe_da_manha = arr.map(fruta => {
return fruta + 's';
});
console.log(cafe_da_manha); // ['maçãs', 'bananas', 'laranjas']
Calma que ainda tem mais...
##########
Arrow functions não só reduzem o tamanho do código. Também estão relacionados ao comportamento do binding do this
O comportamento das arrow functions com o this é diferente do das funções normais. Cada função no JavaScript define seu próprio contexto de this, mas as Arrow functions capturam o this do seu contexto delimitador. Dá uma olhada no código a seguir:
function Pessoa() {
// O construtor de Pessoa() define `this` como uma instância dele mesmo
this.idade = 0;
setInterval(function envelhecer() {
// Quando não se usa strict mode, a função envelhecer()
// define `this` como o objeto global, que é diferente
// do `this` definido pelo construtor de Pessoa()
this.idade++;
}, 1000);
}
var p = new Pessoa();
No ECMAScript 3/5, esse problema era corrigido ao atribuir o valor de this para uma variável.
function Pessoa() {
var self = this;
self.idade = 0;
setInterval(function envelhecer() {
// O callback se refere à variável `self` cujo valor
// é o objeto esperado
self.idade++;
}, 1000);
}
Como mencionado acima, Arrow functions capturam o valor de this do contexto delimitador, logo, o código a seguir funciona como esperado.
function Pessoa() {
this.idade = 0;
setInterval(() => {
this.idade++; // `this` se refere corretamente ao objeto pessoa
}, 1000);
}
var p = new Pessoa();
Leia mais sobre 'Lexical this' em arrows function aqui
3. Parâmetros Default de Funções
ES6 permite que você defina parâmetros default nas definições de funções. A seguir, uma pequena ilustração.
let getPrecoFinal = (preco, imposto = 0.7) => preco + preco * imposto;
getPrecoFinal(500); // 850
4. Operador Spread / Rest
O operador ... é chamado de operador spread ou rest, dependendo de como e onde é usado.
Quando usado com qualquer iterável, ele age como "spread" em elementos individuais.
function foo(x, y, z) {
console.log(x, y, z);
}
let arr = [1, 2, 3];
foo(...arr); // 1 2 3
O outro uso comum de ... é juntar uma série de valores em uma array. Nesse caso, o operador é chamado de "rest".
function foo(...args) {
console.log(args);
}
foo(1, 2, 3, 4, 5); // [1, 2, 3, 4, 5]
5. Extensões de Objetos Literais
ES6 permite que se declare objetos literais com uma sintaxe abreviada para inicializar propriedades de variáveis e definir métodos das funções. Também permite ter índices de propriedades computadas em uma definição de objeto literal.
function getCarro(fabricante, modelo, valor) {
return {
// com a sintaxe abreviada de
// valor da propriedade, você
// pode omitir o valor da propriedade
// se o índice casa com o nome
// da variável
fabricante, // o mesmo que fabricante: fabricante
modelo, // o mesmo que modelo: modelo
valor, // o mesmo que valor: valor
// valores computados agora funcionam
// como objetos literais
['fabricante' + fabricante]: true,
// sintaxe de abreviação de definição
// de método omite a keyword `function`
// e a vírgula
depreciar() {
this.valor -= 2500;
}
};
}
let carro = getCarro('Kia', 'Sorento', 40000);
// output: {
// fabricante: 'Kia',
// modelo:'Sorento',
// valor: 40000,
// fabricanteKia: true,
// depreciar: function()
// }
6. Literais Octais e Binários
ES6 tem um novo suporte para literais octais e binários.
Prefixar um número com 0o ou 0O vai convertê-lo em octal. Olha só:
let oValor = 0o10;
console.log(oValor); // 8
let bValor = 0b10; // 0b ou 0B para binário
console.log(bValor); // 2
7. Desestruturamento de Arrays e Objetos
Desestrututamento ajuda a evitar a necessidade de variáveis temporárias quando lida-se com objetos e arrays.
function foo() {
return [1, 2, 3];
}
let arr = foo(); // [1,2,3]
let [a, b, c] = foo();
console.log(a, b, c); // 1 2 3
function bar() {
return {
x: 4,
y: 5,
z: 6
};
}
let { x: a, y: b, z: c } = bar();
console.log(a, b, c); // 4 5 6
8. super em Objetos
ES6 permite que use o método super em objetos (sem classe) em protótipos. Um exemplo simples a seguir.
var pai = {
foo() {
console.log("Hello do Pai");
}
}
var filho = {
foo() {
super.foo();
console.log("Hello do Filho");
}
}
Object.setPrototypeOf(filho, pai);
filho.foo(); // Hello do Pai
// Hello do Filho
9. Templates Literais e Delimitadores
ES6 introduz uma forma mais fácil ainda de adicionar interpolação analisada automaticamente.
- `${ ... }` usado para renderizar as variáveis.
- ` Contra-aspa é usada como delimitador.
let user = 'Kevin';
console.log(`Olá ${user}!`); // Olá Kevin!
10. for...of vs for...in
for...of itera em objetos iteráveis, tipo arrays.
let apelidos = ['zé', 'bobão', 'cabeçudo'];
apelidos.size = 3;
for (let apelido of apelidos) {
console.log(apelido);
}
// zé
// bobão
// cabeçudo
for...in itera sobre todas as propriedades enumeráveis do objeto.
let apelidos = ['zé', 'bobão', 'cabeçudo'];
apelidos.size = 3;
for (let apelido in apelidos) {
console.log(apelido);
}
// 0
// 1
// 2
// size
11. Map e WeakMap
ES6 introduz uma nova série de estrutura de dados chamados Map e WeakMap. Mas na verdade, a gente usa maps em JavaScript toda hora. Inclusive todo objeto pode ser considerado um Map
Um objeto é feito de índices (sempre strings) e valores, enquanto num Map, qualquer valor (tanto objetos quanto valores primitivos) podem ser usados seja como índice ou como valor. Dá uma olhada nesse código:
var meuMap = new Map();
var indiceString = "uma string",
indiceObj = {},
indiceFuncao = function() {};
// atribuindo valores
meuMap.set(indiceString, "valor associado com 'uma string'");
meuMap.set(indiceObj, "valor associado com indiceObj");
meuMap.set(indiceFuncao, "valor associado com indiceFuncao");
meuMap.size; // 3
// recebendo os valores
meuMap.get(indiceString); // "valor associado com 'uma string'"
meuMap.get(indiceObj); // "valor associado com indiceObj"
meuMap.get(indiceFuncao); // "valor associado com indiceFuncao"
WeakMap
Um WeakMap é um Map onde os índices são referenciados de forma fraca, o que não previne que seus índices sejam coletados pelo garbage collector, o que significa que você não precisa se preocupar com rombos de memória.
Outra coisa a se notar aqui: Em um Weakmap, ao contrário do Map, todo índice deve ser um objeto.
Um WeakMap só tem 4 métodos: delete(indice), has(indice), get(indice) e set(indice, valor)
let w = new WeakMap();
w.set('a', 'b');
// Uncaught TypeError: Invalid value used as weak map key
var o1 = {},
o2 = function(){},
o3 = window;
w.set(o1, 37);
w.set(o2, "azerty");
w.set(o3, undefined);
w.get(o3); // undefined, pois é o valor setado
w.has(o1); // true
w.delete(o1);
w.has(o1); // false
12. Set e WeakSet
Objetos do tipo Set são coleções de valores únicos. Valores duplicados são ignorados, pois a coleção deve possuir apenas valores únicos. Os valores podem ser primitivos ou referências a objetos.
let mySet = new Set([1, 1, 2, 2, 3, 3]);
mySet.size; // 3
mySet.has(1); // true
mySet.add('strings');
mySet.add({ a: 1, b:2 });