ES6, ES2015 — O que mudou?

Carlos Roberto Gomes Junior
7 min readSep 25, 2017

Nos últimos anos o ECMAScript, a especificação do JavaScript, vem apresentando diversas atualizações, tanto em sintaxe como em novos recursos na linguagem. O uso do JavaScript em aplicações de diversas plataformas também vem crescendo a cada ano. Já era esperado que a linguagem teria que amadurecer para acompanhar esse crescimento acelerado.

ES6 ou ES2015?

Em alguns lugares lemos ECMAScript 6, ECMAScript 2015, ES6, ES2015, tudo é a mesma coisa. Neste texto vou utilizar sempre ES6 quando me referir a essa versão.

Em resumo, o ES6 é a sexta versão da especificação que foi finalizada em 2015, dai o termo ES2015.

Status da implementação de diversas versões do ECMAScript

Muitas engines já implementaram boa parte da especificação. Neste link da pra acompanhar o status da implementação do ES6 e as próximas versões em diversos softwares.

A especificação continua em evolução, muitos recursos novos já foram aprovados ou estão em discussão. Você pode acompanhar em detalhes a especificação aqui. Muitas novidades vem pela frente.

Vamos iniciar a nossa jornada!

Declarando variáveis com let e const

let e const são duas novas maneiras de declarar variáveis em JavaScript.

A diferença de declarar com var ou let está no escopo do bloco. Veja no exemplo abaixo:

if(true) {
var z = 1;
}
console.log(z);

Em ES5 z é acessível fora do bloco da instrução if. Já em ES6:

if(true) {
let z = 1;
}
console.log(z); // ReferenceError: z is not defined

Outro recurso muito utilizado para isolar o escopo de variáveis no ES5 era o uso de closures:

(function(){
var value = 'local';
})();
console.log(value); // Uncaught ReferenceError: value is not defined

Em ES6 o escopo é defino dentro de qualquer bloco delimitado por { e }.

Somente isso já bastaria para isolar o escopo:

{
let z = 1;
}
console.log(z); // ReferenceError: z is not defined

Diferente da declaração com var, também não é possível redeclarar uma variável já declarada com let:

let x = 1;
let x = 2; // Uncaught SyntaxError: Identifier 'x' has already been declared

const funciona exatamente da mesma forma que let exceto que as variáveis não podem ser alteradas:

const y = 0;
y = 1; // Uncaught TypeError: Assignment to constant variable

Arrow Functions

As Arrow Functions dentre outras coisas trouxeram uma sintaxe mais simples para declaração de funcões no JavaScript.

Vejamos alguns exemplos:

ES5:

var sum = function(a, b) {
return a + b;
}

ES6:

const sum = (a, b) => {
return a + b;
}

É possível simplificar ainda mais a sintaxe omitindo o return e as chaves na declaração ({}):

const sum = (a, b) => a + b;

Dessa forma o que estiver depois de => será retornado pela função.

Caso a função tenha apenas um parâmetro é possível omitir os parentes nos argumentos:

const squareNumber = n => n * n;

Atenção ao retornar um objeto utlizando essa sintaxe resumida, é necessário colocar o valor retornado entre parênteses:

const nameObj = name => ({name: name});

Outra mudança em relação as funções comuns é que as Arrow Functions não possuem contexto e nem o objeto arguments, a palavra-chave this será sempre referente ao contexto externo a elas.

Por exemplo:

const a = {
name: "a",
b: function() { console.log(this) }
}
a.b(); // {name: "a", b: ƒ}

Já utilizando uma Arrow Function:

const a = {
name: "a",
b: () => { console.log(this) }
}
a.b(); // Window

Destructuring

No ES5, quando necessário atribuir variáveis com os valores de um array ou objeto tinhamos que fazer algo tipo:

const colors = ['red', 'green', 'yellow'];
const red = colors[0];
const dog = { name: 'Joe', owner: 'Carlos' };
const name = dog.name;

Uma declaração destructuring cria variáveis a partir dos valores de um array ou das chaves de um objeto.

Exemplo com um array:

const colors = ['red', 'green', 'yellow'];
const [red, green, yellow] = colors;

Exemplo com um objeto:

const city = { name: 'Ribeirao Preto', temperature: 42 };
const { name, temperature } = city;

Também é possível fazer isso a partir dos parâmetros de uma função:

const city = { name: 'Ribeirao Preto', temperature: 42 };
const printCityInfo = ({ name, temperature }) => `Today temperature in ${name} is ${temperature} degrees`;
printCityInfo(city); // Today temperature in Ribeirao Preto is 42 degrees

Default parameters

Definir parâmetros padrão no ES5 consistia em verificar se o mesmo não existia e criar a variável com o valor padrão, por exemplo:

const setup = (options) => {
options = options || {};
...
}

No ES6 isso ficou mais fácil:

const setup = (options = {}) => {
...
}

Rest parameters

Existem situações onde você precisa acessar todos os argumentos de uma função exeto alguns. No exemplo a seguir temos duas funções que realizam operações matemáticas e outra que executa as operações.

No ES5 tinhamos que recorrer a artifícios da linguagem para fazer isso:

var sum = function() {
return Array.prototype.reduce.call(arguments, function() {
return a + b;
});
}
var multiply = function() {
return Array.prototype.reduce.call(arguments, function() {
return a * b;
});
}
var operation = function(fn) {
// usando slice para separar os argumentos
var values = Array.prototype.slice.call(arguments, 1);
return fn.apply(null, values);
}
operation(sum, 20, 30, 5);
operation(multiply, 20, 30, 5);

Note no exemplo onde utilizamos Array.prototype.reduce.call(arguments, 1), arguments é um objeto similar a um Array, mas não tem os métodos de um, então utilizamos call para executar os métodos de Array em arguments.

No ES6 tudo fica mais simples, utilizando o operador ... podemos definir quais são os argumentos restantes (dai vem o termo rest parameters).

const sum = function() {
return Array.from(arguments).reduce((a, b) => a + b);
}
const multiply = function() {
return Array.from(arguments).reduce((a, b) => a * b);
}
const operation = function(fn, ...values) {
return fn.apply(null, values);
}
operation(sum, 20, 30, 5);
operation(multiply, 20, 30, 5);

Note o operador ... na declaração de parâmetros (fn, ...values) da função operationacima. Ele vai inserir todos os parâmetros restantes da função em um novo array.

Spread Operator em argumentos de funções

O operador ... também pode ser usado quando é necessário passar diversos argumentos para uma função.

No ES5 isso seria algo como:

var numbers = [4, 5, 2, 16, 3, 5];
var max = Math.max.apply(null, numbers);
console.log(min); // 16

Em ES6:

const numbers = [4, 5, 2, 16, 3, 5];
const max = Math.max(...numbers);
console.log(min); // 16

Também é possivel mesclar com outros argumentos:

const max = Math.max(6, 3, 8, ...[12, 67, 13]);

Rest & Spread Properties

Vimos acima que é possível utilizar destructuring para definir variáveis a partir das propriedades de um objeto.

const movie = {
name: "Star Gate",
year: 1994,
director: "Roland Emmerich",
};
const { name, year, director } = movie;

Também é possível utlizar o operador ... para definir algumas variáveis e transformar as restantes em um novo objeto.

const { name, ...others } = movie;
console.log(others); // {year: 1994, director: "Roland Emmerich"}

Algo inverso pode ser feito, mesclar as propriedades de um objeto com outro (spread properties).

Em ES5 mesclar dois objetos seria algo como no exemplo abaixo:

var a = {b: 1, c: 2};
var d = {e: 3, f: 4};
var g = {};
for(var i in a) {
g[k] = a[k];
}
for(var j in a) {
g[j] = d[j];
}
console.log(g); // {e: 3, f: 4, b: 1, c: 2}

No ES6 temos duas formas de fazer isso.

Utilizando Object.assign para criar um novo objeto:

Object.assign({}, a, d);

Utilizando o operador ...:

let g = {...a, ...b};
console.log(g); // {e: 3, f: 4, b: 1, c: 2}

Template Strings

As template strings vieram para simplificar a interpolação de variáveis e expressões em strings.

Enquanto no ES5 era feito da seguinte forma:

var name = 'Calos';
var country = 'Brazil';
var phrase = 'My name is ' + name + '.\n I\'m from ' + country;

No ES6:

const name = 'Calos';
const country = 'Brazil';
const phrase = `My name is ${name}.
I'm from ${country}`;

Note que as template strings são delimitadas pelos caracteres `` e as variáveis são interpoladas com ${variable}. Note também que as quebras de linha são interpretadas.

Classes

A sintaxe de classes no ES6 veio para simplificar a orientação a objetos baseada em prototype.

O que antes era feito da seguinte forma:

function Thing(name) {
this.name = name;
}
Thing.prototype.getInfo = function() {
return `name: ${this.name}`;
}

No ES6:

class Thing {
constructor(name) {
this.name = name;
}
getInfo() {
return `name: ${this.name}`;
}
}

Fazer herança no ES5 também era algo bem complicado, no ES6 basta estender uma classe de outra:

class Human extends Thing {
constructor(name, age) {
super(name);
this.age = age;
}
getInfo() {
return `${super.getInfo()}, age: ${this.age}`;
}
}

Note que no método constructor, super é opcional, porem se for utilizado é necessário executar antes de outras declarações.

Também é possível definir classes com essa sintaxe alternativa:

const Thing = class {
constructor(name) {
this.name = name;
}
getInfo() {
return `name: ${this.name}`
}
}

Modules

Uma das maiores novidades com o ES6 foi a introdução de modulos. O Conceito de modulo no ES6 foi baseado em outras implementações como CommonJS e AMD.

Basicamente um modulo é um arquivo que exporta valores.

math.js

export const PI = Math.PI;
export const PHI = 1.618;
const Math = {
sum: (a, b) => a + b;
}
export default Math;

main.js

import Math, { PI, PHI } from './math.js';console.log(PI); // 3.1415
console.log(PHI); // 1.618
console.log(Math.sum(10, 3)); // 13

A declaração export define valores que serão exportados e import valores de um módulo que serão importados. export default define o valor padrão que será exportado pelo módulo.

É possível alterar o nome da referência do valor importado utilizando as:

import Math as M from '.math.js';console.log(M.sum(10, 3)); // 13

Dynamic Imports

Existe também uma proposta para implementação de imports dinamicos, que poderia ser usado para carregar codigo sob demanda ou de forma condicional.

import('./math.js').then((module) => {
console.log(module.PI) // 3.1415;
});

Nessa sintaxe o import retorna uma Promise com o módulo carregado.

Finalizando

Esse artigo foi uma pequena introdução a algumas das principais mudanças e novidades no ES6. Não abordei aqui temas mais avançados como Promises, Iterators, Generators, Symbol, Map, Proxies, etc, mas futuramente pretendo escrever outros artigos detalhando mais estas e outras funcionalidades.

Não deixem de comentar caso tenham dúvidas, sugestões ou correções.

Obrigado e até a próxima!

--

--

Carlos Roberto Gomes Junior

JavaScript Developer | Functional Programming Lover | Blockchain Enthusiastic