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.
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 operation
acima. 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!