ES6, ES2015 — O que mudou?

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

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

if(true) {
let z = 1;
}

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

(function(){
var value = 'local';
})();

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

Somente isso já bastaria para isolar o escopo:

{
let z = 1;
}

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];

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 ${city} is ${temperature} 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;
});
}

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

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",
};

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 = {};

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

No ES6:

class Thing {
constructor(name) {
this.name = 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;
}

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

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;

main.js

import Math, { PI, PHI } from './math.js';

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';

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!

iClinic

Junte grandes pessoas e elas farão grandes coisas, estamos aqui para dividir algumas grandes lições que aprendemos!

Thanks to João Milton Lavoier Filho

Carlos Roberto Gomes Junior

Written by

JavaScript Developer | Functional Programming Lover | Blockchain Enthusiastic

iClinic

iClinic

Junte grandes pessoas e elas farão grandes coisas, estamos aqui para dividir algumas grandes lições que aprendemos!

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade