ECMAScript 6, também conhecido como ECMAScript 2015, é a última versão do padrão ECMAScript. ES6 é uma atualização significativa para a linguagem e é a primeira atualização desde que ES5 se tornou um padrão em 2009. A implementação dessas funcionalidades nas principais engines de JavaScript já está em andamento.
Veja o padrão ES6 para a especificação completa da linguagem ECMAScript 6.
ES6 inclui as seguintes novas funcionalidades:
- Setas (Arrows)
- Classes
- Objetos Literais Aprimorados (Enhanced Object Literals)
- Template Strings
- Destructuring
- Default + Rest + Spread
- let + const
- Iterators + for..of
- Geradores (Generators)
- Unicode
- Módulos (Modules)
- Carregadores de Módulos (Module Loaders)
- Map + Set + WeakMap + WeakSet
- Proxies
- Symbols
- Subclassable Built-ins
- Math + Number + String + Array + APIs de Objetos
- Binário e Octal literais
- Promises
- Reflect API
- Tail Calls
Setas são a maneira simplificada para funções usando a sintaxe =>
. Elas são sintaticamente
similares ao recurso relacionado em C#, Java 8 e CofeeScript. Suportam tanto corpos de blocos
statement, quanto os de expressões que retornam o valor da expressão. Diferentemente de funções,
Setas compartilham o mesmo léxico this
, tanto como o código que o envolve (surrounding code).
// Expressão
var odds = evens.map(v => v + 1);
var nums = evens.map((v, i) => v + i);
var pairs = evens.map(v => ({even: v, odd: v + 1}));
// Statement
nums.forEach(v => {
if (v % 5 === 0)
fives.push(v);
});
// Léxico "this"
var bob = {
_name: "Bob",
_friends: [],
printFriends() {
this._friends.forEach(f =>
console.log(this._name + " knows " + f));
}
}
Classes ES6 são simplesmente um "docinho" para o padrão OO baseado em protótipos. Ter uma forma declarativa, conveniente e simples de padrões de classes faz delas mais fáceis de serem usadas e encoraja a interoperabilidade. Classes suportam herança baseada em protótipos, chamadas super, métodos instanciados e estáticos e construtores.
class SkinnedMesh extends THREE.Mesh {
constructor(geometry, materials) {
super(geometry, materials);
this.idMatrix = SkinnedMesh.defaultMatrix();
this.bones = [];
this.boneMatrices = [];
//...
}
update(camera) {
//...
super.update();
}
get boneCount() {
return this.bones.length;
}
set matrixType(matrixType) {
this.idMatrix = SkinnedMesh[matrixType]();
}
static defaultMatrix() {
return new THREE.Matrix4();
}
}
Object Literals são estendidos para dar suporte à especificação de prototype na construção,
simplificações para atribuições foo: foo
, definição de métodos, chamadas super e computar propriedades
de nome com expressões. Juntos, estes fazem Objetos Literais e declarações de classes mais próximos e
o benefício do design baseado em objetos com as mesmas conveniências.
var obj = {
// __proto__
__proto__: theProtoObj,
// Simplificação para ‘handler: handler’
handler,
// Métodos
toString() {
// Chamadas super
return "d " + super.toString();
},
// Nomes de propriedades computados (dinamicos)
[ 'prop_' + (() => 42)() ]: 42
};
Template Strings proveem "açúcar sintático" para montar strings. São similares a interpolação de strings em Perl, Python e outros. Opcionalmente, uma tag pode ser adicionada para permitir a construção da string ser customizada, evitando ataques de injeção ou construindo estruturas de dados de alto nível a partir de conteúdos de strings.
// Criação básica de string literal
`In JavaScript '\n' is a line-feed.`
// Strings multilinhas
`In JavaScript this is
not legal.`
// Interpolation de string
var name = "Bob", time = "today";
`Hello ${name}, how are you ${time}?`
// Montar um prefixo de requisição HTTP pode ser usado para interpretar substutuições e construção
POST`http://foo.org/bar?a=${a}&b=${b}
Content-Type: application/json
X-Credentials: ${credentials}
{ "foo": ${foo},
"bar": ${bar}}`(myOnReadyStateChangeHandler);
Desestruturação permite binding usando matching de padrões com suporte para matching de arrays e
objetos. Desestruturação é "fail-soft", similar ao lookup padrão de objeto foo["bar"]
, produzindo
valores undefined
quando não encontrado.
// Matching de array
var [a, , b] = [1,2,3];
// Matching de objeto
var { op: a, lhs: { op: b }, rhs: c }
= getASTNode()
// shorthand para matching de objeto faz
// bind para `op`, `lhs` e `rhs` no escopo
var {op, lhs, rhs} = getASTNode()
// Pode ser usado na posição do parametro
function g({name: x}) {
console.log(x);
}
g({name: 5})
// Desestruturação fail-soft
var [a] = [];
a === undefined;
// Desestruturação fail-soft com defaults
var [a = 1] = [];
a === 1;
Valores padrão de parâmetros avaliados pelo "chamador". Transforma um array em argumentos consecutivos em uma chamada de função. Vincula (bind) parâmetros para array. Rest troca a necessidade de argumentos e endereça casos de espaços comuns mais diretamente.
function f(x, y=12) {
// y é 12 se não for passado (ou passado como undefined)
return x + y;
}
f(3) == 15
function f(x, ...y) {
// y é um Array
return x * y.length;
}
f(3, "hello", true) == 6
function f(x, y, z) {
return x + y + z;
}
// Passa cada elementos do array como argumento
f(...[1,2,3]) == 6
let
é o novo var
. const
é de atribuição única (single-assignment). Restrições de Estático
previnem o uso antes da atribuição.
function f() {
{
let x;
{
// OK, escopo de bloco
const x = "sneaky";
// error, const
x = "foo";
}
// error: já declarado no bloco
let x = "inner";
}
}
Objetos iteradores permitem iteradores personalizações como CLR IEnumerable ou Java Iterable.
Generalização de for..in
para iteração customizada com for..of
. Não é preciso array, o que
permite padrões lazy de design como LINQ.
let fibonacci = {
[Symbol.iterator]() {
let pre = 0, cur = 1;
return {
next() {
[pre, cur] = [cur, pre + cur];
return { done: false, value: cur }
}
}
}
}
for (var n of fibonacci) {
// trunca a sequência em 1000
if (n > 1000)
break;
console.log(n);
}
A iteração é baseada nessas interfaces (usando a sintaxe de tipo de TypeScript somente para exemplificar):
interface IteratorResult {
done: boolean;
value: any;
}
interface Iterator {
next(): IteratorResult;
}
interface Iterable {
[Symbol.iterator](): Iterator
}
Generators simplificam iterações usando function*
and yield
. Uma função declarada como
function*
retorna uma instância de Generator. Generators são subtipos de iteradores que incluem
next
e throw
. Estes valores permitem voltar no gerador, então yield
é uma forma de expressão
que retorna um valor -- ou o lança (throws).
Nota: Também pode ser usado para habilitar programação assíncrona "‘await’-like", tal como a
proposta de await
em ES7.
var fibonacci = {
[Symbol.iterator]: function*() {
var pre = 0, cur = 1;
for (;;) {
var temp = pre;
pre = cur;
cur += temp;
yield cur;
}
}
}
for (var n of fibonacci) {
// truncate the sequence at 1000
if (n > 1000)
break;
console.log(n);
}
A iteração é baseada nessas interfaces (usando a sintaxe de tipo de TypeScript somente para exemplificar):
interface Generator extends Iterator {
next(value?: any): IteratorResult;
throw(exception: any);
}
Foram feitas adições para suporte total a Unicode, incluindo a nova forma literal de Unicode em
string e o novo modo de expressões regulares u
para lidar com code points, tal como novas APIs
para processar strings no nível em código 21bit. Essas adições permitem o desenvolvimento de
aplicativos globais em JavaScript.
// mesmo que em ES5.1
"𠮷".length == 2
// novo ‘u’ de expressões regulares
"𠮷".match(/./u)[0].length == 2
// nova maneira
"\u{20BB7}"=="𠮷"=="\uD842\uDFB7"
// novo String ops
"𠮷".codePointAt(0) == 0x20BB7
// for-of itera pontos de código (code points)
for(var c of "𠮷") {
console.log(c);
}
Suporte a Módulos em nível de linguagem para definição de componentes. Codifica padrões de loaders de módulos populares em JavaScript (AMD, CommonJS). Comportamente em runtime definido por um loader que pode ser definido. Modelo assíncrono implícito -- nenhum código executa até os módulos requeridos ficarem disponíveis e processados.
// lib/math.js
export function sum(x, y) {
return x + y;
}
export var pi = 3.141593;
// app.js
import * as math from "lib/math";
alert("2π = " + math.sum(math.pi, math.pi));
// otherApp.js
import {sum, pi} from "lib/math";
alert("2π = " + sum(pi, pi));
Algumas features adicionais incluem export default
e export *
:
// lib/mathplusplus.js
export * from "lib/math";
export var e = 2.71828182846;
export default function(x) {
return Math.log(x);
}
// app.js
import ln, {pi, e} from "lib/mathplusplus";
alert("2π = " + ln(e)*pi*2);
Module loaders suportam:
- Carregamento Dinâmico (Dynamic loading)
- Isolamento de Estado (State isolation)
- Isolamento Global de Namespace (Global namespace isolation)
- Hooks de Compilação (Compilation hooks)
- Virtualização Aninhada (Nested virtualization)
O loader padrão de módulo pode ser configurado e novos loaders podem ser construídos para processar e carregar códigos em contextos isolados ou restringidos.
// Carregamento Dinâmico -- ‘System’ é o loader padrão
System.import('lib/math').then(function(m) {
alert("2π = " + m.sum(m.pi, m.pi));
});
// Cria sandbox de execução
var loader = new Loader({
global: fixup(window) // substitui ‘console.log’
});
loader.eval("console.log('hello world!');");
// Manipula módulo de cache diretamente
System.get('jquery');
System.set('jquery', Module({$: $})); // CUIDADO: ainda não finalizado
Estruturas de dados eficientes para algoritmos comuns. WeakMaps proveem coleções de pares chave/valor em que as chaves são fracamente referenciadas (weakly referenced).
// Sets
var s = new Set();
s.add("hello").add("goodbye").add("hello");
s.size === 2;
s.has("hello") === true;
// Maps
var m = new Map();
m.set("hello", 42);
m.set(s, 34);
m.get(s) == 34;
// Weak Maps
var wm = new WeakMap();
wm.set(s, { extra: 42 });
wm.size === undefined
// Weak Sets
var ws = new WeakSet();
ws.add({ data: 42 });
// Porque o objeto adicionado não possui outras referências, não será mantido no conjunto
Proxies permitem a criação de objetos com a gama completa de comportamentos de host objects de JavaScript. Podem ser usados para interceptação, virtualização de objetos, logging/profiling etc.
// Proxying de um objeto normal
var target = {};
var handler = {
get: function (receiver, name) {
return `Hello, ${name}!`;
}
};
var p = new Proxy(target, handler);
p.world === 'Hello, world!';
// Proxying de um objeto de função
var target = function () { return 'I am the target'; };
var handler = {
apply: function (receiver, ...args) {
return 'I am the proxy';
}
};
var p = new Proxy(target, handler);
p() === 'I am the proxy';
Existem "armadilhas" para todas as metaoperações a nível de runtime:
var handler =
{
get:...,
set:...,
has:...,
deleteProperty:...,
apply:...,
construct:...,
getOwnPropertyDescriptor:...,
defineProperty:...,
getPrototypeOf:...,
setPrototypeOf:...,
enumerate:...,
ownKeys:...,
preventExtensions:...,
isExtensible:...
}
Symbols são um novo tipo de primitivo que permitem controle de acesso ao estado de objeto. Permitem
que se use propriedades como chave tanto para string
(como em ES5), quanto symbol
. Opcionalmente,
é possível usar o parâmetro description
em debugging -- mas não é parte da identidade. Symbols são
únicos (como gensym), mas não privados, já que podem ser expostos através de características de
reflection, como Object.getOwnPropertySymbols
.
var MyClass = (function() {
// módulo de símbolo em escopo
var key = Symbol("key");
function MyClass(privateData) {
this[key] = privateData;
}
MyClass.prototype = {
doStuff: function() {
... this[key] ...
}
};
return MyClass;
})();
var c = new MyClass("hello")
c["key"] === undefined
Em ES6, built-ins como Array
, Date
e Element
s DOM podem ser "subclasseados".
Construção de Objeto para a função chamada Ctor
agora usa 2 fases -- ambas virtualmente
"despachadas" (dispatched):
- Chama
Ctor[@@create]
para alocar o objeto, instalando qualquer comportamento especial - Invoca o construtor na nova instância para inicializar
O conhecido símbolo @@create
é avaliado via Symbol.create
. Built-ins agora expõem explicitamente
seus @@create
.
// Pseudocódigo de Array
class Array {
constructor(...args) { /* ... */ }
static [Symbol.create]() {
// Instala [[DefineOwnProperty]]
// para magicamente atualizar 'length'
}
}
// Código de usuário do Array subclasseado
class MyArray extends Array {
constructor(...args) { super(...args); }
}
// "new" em 2 fases:
// 1) Chama @@create para alocar o objeto
// 2) Invoca o construtor na nova instância
var arr = new MyArray();
arr[1] = 12;
arr.length == 2
Muitas novas adições de bibliotecas, incluindo bibliotecas core d Math, helpers de conversão de
Array, helpers de String e Object.assign
para copiar.
Number.EPSILON
Number.isInteger(Infinity) // false
Number.isNaN("NaN") // false
Math.acosh(3) // 1.762747174039086
Math.hypot(3, 4) // 5
Math.imul(Math.pow(2, 32) - 1, Math.pow(2, 32) - 2) // 2
"abcde".includes("cd") // true
"abc".repeat(3) // "abcabcabc"
Array.from(document.querySelectorAll('*')) // Returns a real Array
Array.of(1, 2, 3) // Similar to new Array(...), but without special one-arg behavior
[0, 0, 0].fill(7, 1) // [0,7,7]
[1, 2, 3].find(x => x == 3) // 3
[1, 2, 3].findIndex(x => x == 2) // 1
[1, 2, 3, 4, 5].copyWithin(3, 0) // [1, 2, 3, 1, 2]
["a", "b", "c"].entries() // iterator [0, "a"], [1,"b"], [2,"c"]
["a", "b", "c"].keys() // iterator 0, 1, 2
["a", "b", "c"].values() // iterator "a", "b", "c"
Object.assign(Point, { origin: new Point(0,0) })
2 novas formas de literais numéricos foram adicionados para binário (b
) e octal (o
).
0b111110111 === 503 // true
0o767 === 503 // true
Promises são a biblioteca para programação assíncrona. Promises são a primeira representação de classe para um valor que pode estar disponível no futuro.
function timeout(duration = 0) {
return new Promise((resolve, reject) => {
setTimeout(resolve, duration);
})
}
var p = timeout(1000).then(() => {
return timeout(2000);
}).then(() => {
throw new Error("hmm");
}).catch(err => {
return Promise.all([timeout(100), timeout(200)]);
})
API completa para reflection expõe metaoperações em nível de runtime em objetos. Isso é efetivamente o inverso da Proxy API e permite realizar chamadas correspondendo às mesmas metaoperações, como as proxy traps. Especialmente útil para implementar proxies.
// Ainda sem exemplo
Chamadas em tail-position (no final da instrução) garantem o não crescimento da stack de forma descontrolada. Fazer algoritmos recursivos de forma segura em face a inputs descontrolados.
function factorial(n, acc = 1) {
'use strict';
if (n <= 1) return acc;
return factorial(n - 1, n * acc);
}
// Stack overflow na maioria das implementações atuais,
// mas seguro em inputs arbitrários em ES6
factorial(100000)
``