io.js – o famoso Node.js O io.js obteve esses recursos antes do Node.js seguindo agressivamente as versões mais recentes do mecanismo V8 JavaScript. Como um observador externo, tirei algumas horas para dar uma olhada e relatarei minhas descobertas aqui.

Instalação

As distribuições binárias do io.js estão disponíveis no site página iniciale o senhor pode fazer o download de um binário para Linux, Windows, Mac ou criá-lo a partir do código-fonte. No entanto, os instaladores binários sobrescreverão o arquivo node e npm em seu sistema se o senhor tiver o Node.js instalado. Portanto, recomendo usar o nvm para instalar o io.js de forma livre de conflitos. A instalação do nvm é muito fácil se o senhor não tiver feito isso antes. Se e quando o senhor tiver nvm, basta fazer

$ nvm install io.js
######################################################################## 100.0%
WARNING: checksums are currently disabled for io.js
Now using io.js v1.0.3


Verifique se funcionou:

$ iojs
>


Voilà! Observe que node tem como alias o nome iojs, e npm ainda é chamado de npm.

Visão geral dos recursos do ES6

Embora alguns pessoas têm foram usando ES6 por um tempo por meio de transpiladores, quando trabalho com código transpilado, parece que estou tendo que depurar simultaneamente duas versões do código – o senhor não sabe o que está fazendo? A depuração já é difícil o suficiente com apenas uma versão. Por esse motivo, ter suporte nativo o torna muito mais atraente para mim.

O página ES6 do io.js fornece informações sobre as alterações que foram feitas no suporte a ES6 no mecanismo. Eles eliminaram a função --harmony que no Node 0.11+ era necessário incluir se o usuário quisesse usar qualquer recurso ES6. No io.js, o senhor os obtém imediatamente! A lista atual de recursos do ES6 ativados por padrão é a seguinte:

  • let declaração
  • const declaração
  • Map e Set
  • WeakMap e WeakSet
  • Geradores
  • Literais binários e octais
  • Promessas
  • Alguns métodos adicionais de string
  • Símbolos
  • Strings de gabarito

Eles também adicionaram o --es_staging que permitiria que o senhor obtivesse acesso a recursos que estão prontos, mas que ainda não foram bem testados. Para os recursos que estão sendo implementados, o senhor terá de obter acesso a cada recurso individualmente usando o sinalizador de harmonia correspondente a ele. O senhor pode obter a lista de sinalizadores de recursos de harmonia via:

$ iojs --v8-options|grep "harmony"
  --es_staging (enable all completed harmony features)
  --harmony (enable all completed harmony features)
  --harmony_shipping (enable all shipped harmony fetaures)
  --harmony_modules (enable "harmony modules (implies block scoping)" (in progress))
  --harmony_arrays (enable "harmony array methods" (in progress))
  --harmony_array_includes (enable "harmony Array.prototype.includes" (in progress))
  --harmony_regexps (enable "harmony regular expression extensions" (in progress))
  --harmony_arrow_functions (enable "harmony arrow functions" (in progress))
  --harmony_proxies (enable "harmony proxies" (in progress))
  --harmony_sloppy (enable "harmony features in sloppy mode" (in progress))
  --harmony_unicode (enable "harmony unicode escapes" (in progress))
  --harmony_tostring (enable "harmony toString")
  --harmony_numeric_literals (enable "harmony numeric literals")
  --harmony_strings (enable "harmony string methods")
  --harmony_scoping (enable "harmony block scoping")
  --harmony_classes (enable "harmony classes (implies block scoping & object literal extension)")
  --harmony_object_literals (enable "harmony object literal extensions")
  --harmony_templates (enable "harmony template literals")


Agora, vamos nos aprofundar nos recursos individuais.

let e const

O let e const só estão disponíveis no modo estrito. Portanto, coloque "use strict" na parte superior de cada arquivo JS em que o senhor deseja usá-las.

O let é um substituto para a declaração var que tem escopo lexical. Isso significa que, embora uma variável definida com var é visível para a função na qual foi declarada, let é visível apenas para o bloco de código no qual foi declarado. Em JavaScript, um bloco de código é uma declaração composta entre { e } que contém zero ou mais instruções. É comum usar blocos de código dentro de instruções if, loops for, loops while e como o corpo de uma definição de função. Porém, também é possível escrever um bloco de código autônomo.

Aqui está um exemplo de let:

"use strict"
if (player.partner){
  let partner = player.partner
  // do stuff with partner here
}
console.log(parter) // this throws partner is not defined


Aqui está let em um loop for:

"use strict"
for (let i = 0; i < 10; i++){
  console.log(i)
}
console.log(i) // this throws i is not defined


const é como se o let exceto pelo fato de que, uma vez declarada, a variável não pode ser reatribuída a outro valor.

"use strict"
const ITERATIONS_TO_RUN = 10
ITERATIONS_TO_RUN = 12 // throws TypeError: Assignment to constant variable.


Mapa e conjunto

O ES6 introduziu o Map e Set estruturas de dados para sua conveniência. Agora o senhor deve estar se perguntando: por que precisamos de um mapa? O que há de errado em usar literais de objetos como mapas? Bem, já se argumentou que o um objeto não é um hash (ou um mapa). A versão resumida é que um objeto herda todos os Object.prototypeo que, na maioria dos casos, não é desejável se o senhor quiser usá-lo como um mapa.

Agora, aqui está um exemplo de uso do Map:

> var m = new Map
undefined
> m.set('name', 'Bobby')
{}
> m.get('name')
Bobby
> m.size
1
> m.set('age', 5)
{}
> m.has('age')
true
> m.has('foobar')
false
> m.forEach(function(value, key){ console.log(key + ' maps to ' + value) })
name maps to Bobby
age maps to 5
> m.get('hasOwnProperty') // avoids the `hasOwnProperty` trap
undefined
> m.clear()
undefined
> m.size
0


E aqui está o Set em ação:

> var s = new Set
undefined
> s.add(1)
{}
> s.size
1
> s.add(2)
{}
> s.size
2
> s.add(1) // adding a duplicate here
{}
> s.size   // no change in size
2
> s.has(1)
true
> s.has(2)
true
> s.has(3)
false
> s.forEach(function(n){ console.log('Set has ' + n) })
Set has 1
Set has 2


WeakMap e WeakSet

WeakMap e WeakSet são novos tipos de dados que espelham o Map e Setmas, ao contrário de Map e Set – que podem ser implementados como polyfills – esses só podem ser implementados nativamente. A palavra “fraco” refere-se ao referências fracas. A referência fraca é uma referência de objeto que é ignorada pelo coletor de lixo. Se existirem apenas referências fracas – não mais referências fortes – apontando para o objeto em questão, esse objeto poderá ser destruído e sua memória será devolvida.

Vamos falar sobre WeakSet primeiro, porque é mais fácil de explicar. A WeakSeté um subconjunto de Set‘s. No entanto, o senhor não pode armazenar valores primitivos nela:

> var ws = new WeakSet
undefined
> ws.add(1)
TypeError: Invalid value used in weak set


Isso faz sentido porque os valores primitivos são armazenados por valor, não por referência, e não faria sentido sequer falar em referências fracas. Portanto, em vez disso, o senhor precisará colocar objetos nele:

> var bob = {name: 'Bob'}
undefined
> var jen = {name: 'Jen'}
undefined
> ws.add(bob)
{}
> ws.add(jen)
{}
> ws.has(bob)
true
> ws.has(jen)
true
> var jim = {name: 'Jim'}
undefined
> ws.has(jim)
false
> ws.delete(jen)
true
> ws.has(jen)
false


WeakSet não tem size ou uma forma de iterar seus membros

> ws.size
undefined
> ws.forEach(function(item){ console.log('WeakSet has ' + item)})
TypeError: undefined is not a function
> ws.forEach
undefined


Isso ocorre justamente porque as referências são fracas e, como tal, os objetos podem ser destruídos sem aviso prévio, o que impossibilitaria o acesso a eles. Um possível uso do WeakSet é armazenar um conjunto de elementos DOM relacionados sem se preocupar com vazamentos de memória quando os elementos são removidos do documento.

A WeakMap é como se o Map exceto pelo fato de que todas as suas chaves são referências fracas. Elas também não podem ser valores primitivos.

var wm = new WeakMap
> var person = {name: 'Bob'}
undefined
> var creditCard = {type: 'AMEX', number: 123456789}
undefined
> wm.set(person, creditCard)
{}
> wm.get(person)
{ type: 'AMEX', number: 123456789 }


Assim como no caso do Set, não há como obter o size do WeakMap ou iterar sobre suas chaves ou valores:

> wm.size
undefined
> wm.forEach
undefined


Quando o aplicativo deixa de ter uma referência forte ao person, sua entrada em wm poderia ser destruída, e o creditCard poderia, por sua vez, ser destruído também. Leia mais sobre WeakMap e WeakSet.

for-of

Além do clássico for-in clássico, o ES6 adicionou a declaração for-of que permite iterar com sucesso os valores de arrays, iteráveis e geradores. Os dois últimos serão discutidos a seguir.

Aqui está for-of iterando sobre uma matriz:

> var arr = [1, 2, 3]
undefined
> for (var n of arr) console.log(n)
1
2
3


Iteráveis e Iteradores

Portanto, o senhor também pode usar o for-of para iterar sobre iteráveis.

Mas o que é um iterável?

Um iterável é um objeto que tem um método associado que inicializa e retorna um iterador. A forma como o senhor associa esse método a um objeto é:

var myObj = {}
myObj[Symbol.iterator] = function(){  // I'll cover symbols later
  return new MyIterator
} 


Mas o que é um iterador?

Um iterador é um objeto que adere ao protocolo de iteradores – que requer apenas um método:

  • next() – que avança para o próximo item da sequência toda vez que é chamado e retorna um objeto que contém duas propriedades
  • done – um booleano que é verdadeiro se e somente se a sequência já tiver terminado
  • value – o valor atual na sequência

Como exemplo, veja abaixo como eu consegui tornar iterável uma implementação simples de lista de links personalizada:

function LLNode(value){
  this.value = value
  this.next = null
}
LLNode.prototype[Symbol.iterator] = function(){
  var iterator = {
    next: next
  }
  var current = this
  function next(){
    if (current){
      var value = current.value
      var done = current == null
      current = current.next
      return {
        done: done,
        value: value
      }
    }else{
      return {
        done: true
      }
    }
  }
  return iterator
}

var one = new LLNode(1)
var two = new LLNode(2)
var three = new LLNode(3)
one.next = two
two.next = three

for (var i of one){
  console.log(i)
}


O resultado desse programa é

1
2
3


Geradores

Os geradores permitem que o senhor escreva um iterável de forma sucinta e facilmente compreensível. Eles também permitem que o senhor represente sequências infinitas.

Veja como eu poderia escrever um gerador que itera todos os números inteiros a partir de 0:

function *naturalNumbers(){
  var n = 0
  while (true){
    yield n++
  }
}


Observe que o function * e a sintaxe do yield indicam que se trata de uma declaração do função geradora em vez de uma função normal. Quando o senhor chama uma função geradora, recebe de volta um gerador que implementa o protocolo do iterador:

> var gen = naturalNumbers()
{}
> gen.next()
{ value: 0, done: false }
> gen.next()
{ value: 1, done: false }


Ele também é um iterável! O senhor pode verificar isso: se chamar seu método iterador, receberá de volta o próprio gerador:

> gen[Symbol.iterator]() === gen
true


Mas a maneira mais eficiente de iterar sobre um iterável, é claro, é por meio da função for-of declaração:

for (var n of naturalNumbers()){
  console.log(n)
}


Ops! Loop infinito (facepalm).

Os geradores também são interessantes porque são uma solução (entre várias) para o problema do callback hell. Notavelmente, co e koa são estruturas que fazem uso intenso de geradores, e ambas funcionam no io.js imediatamente;
Ler mais para obter tratamentos mais detalhados sobre os geradores.

Números binários e octais

Os números binários são prefixados com 0b, e os números octais são prefixados com 0O – ou seja, “zero” “O”.

console.log(0b100)
console.log(0O100)


O programa acima produz resultados:

4
64


Promessas

O desenvolvimento de promessas foi um esforço muito popular, começando como bibliotecas ou componentes em várias estruturas. Atualmente, existem bibliotecas estabelecidas como RSVP, Qe Pássaro azul. A maioria das principais estruturas tem promessas incorporadas. Há um padrão para promessas chamado Promessas A+ ao qual a maioria das principais implementações aderem. Para completar, as promessas foram incorporadas ao próprio tempo de execução! A história por trás das promessas é bastante inspiradora.

Abaixo está um exemplo de como transformar uma biblioteca cliente http baseada em callback em uma função que retorna uma promessa:

var request = require('superagent')

fetch('http://iojs.org')
  .then(function(reply){
    console.log('Returned ' + reply.text.length + ' bytes.')
  })

function fetch(url){
  return new Promise(function(resolve, reject){
    request(url).end(function(err, reply){
      if (err){
        reject(err)
      }else{
        resolve(reply)
      }
    })
  })
}


As promessas também podem ser usadas de forma eficaz com geradores – que é a estratégia empregada pelo co. Ler este tutorial para obter uma explicação mais detalhada das promessas.

Novos métodos String

Alguns novos métodos foram adicionados à versão nativa do String objeto.

  • String.fromCodePoint(number) e .codePointAt(idx) são como String.fromCharCode e .charCodeAt(idx) exceto pelo fato de que eles suportam unicode e, portanto, os pontos de código alto são traduzidos em caracteres de vários bytes

    > s = String.fromCodePoint(194564)
    '你'
    > s.codePointAt(0)
    194564
    

  • startsWith(s) e endsWith(s)

    > 'Hello, world!'.startsWith('Hello')
    true
    > 'Hello, world!'.endsWith('!')
    true
    

  • repeat(n)

    > 'foobar'.repeat(5)
    'foobarfoobarfoobarfoobarfoobar'
    

  • normalize() – retorna o formulário de normalização unicode da string. Para realmente entender o que isso significa, leia sobre equivalência unicode.

Símbolos

O nome symbol pode ser confuso porque esses símbolos não são como os de Ruby ou Smalltalk. Os símbolos no ES6 são usados como propriedades de objetos ocultos. Se o senhor for um Pythonista: pense em métodos mágicos com sublinhado duplo.

var secretMethod = Symbol('secret')
var obj = {}
obj[secretMethod] = function(){
  return 'foobar'
}
obj[secretMethod]() // returns `foobar`


Agora, secretMethod não aparecerá em um for-in que percorre as propriedades do objeto. De fato, nenhuma propriedade de string corresponde ao símbolo referenciado por secretMethod e não há como acessar o método sem ter uma referência ao símbolo. Há símbolos globais “bem conhecidos” no sistema, como Symbol.iterator – que vimos ser usado para associar um objeto a seu iterador. O senhor pode fazer isso de qualquer maneira, leia mais sobre os símbolos.

Strings de modelo e strings de várias linhas

As strings de modelo são emprestadas da interpolação de strings do Ruby e do Perl. Isso evita que os desenvolvedores tenham que juntar pedaços de strings de forma desajeitada, o que geralmente resulta em muitas aspas.

> var name="Bobby"
undefined
> `Hello, ${name}!`
'Hello, Bobby!'


Observe que as cadeias de caracteres de modelo são delimitadas por traços ascendentes “`” em vez de aspas simples ou duplas. O que é interessante para mim é que agora o senhor pode escrever strings de várias linhas:

var age = 5
var sql = `
select
  name
from
  people
where
  age > ${age};
`


As strings de modelo têm mais um recurso: permitir que uma função personalizada avalie o modelo em questão. Isso é útil em situações que exigem escape de parâmetros específicos, como na higienização de parâmetros SQL para evitar ataques de injeção de SQL.

var age = 5
var sql = sqlSanitize`
select
  name
from
  people
where
  age > ${age};
`


O senhor pode ler mais para tratamentos detalhados de strings de modelo.

Recursos notáveis por trás das bandeiras

Alguns dos recursos notáveis ainda estão marcados como em andamento no io.js – versão 1.0.3 no momento da redação deste texto – são:


Impressão geral

Estou otimista em relação ao estado dos recursos do ES6 no io.js. Gosto do fato de todos esses recursos funcionarem imediatamente, sem nenhum sinalizador especial. Mentalmente, essa designação torna esses recursos legítimos. Na maioria das vezes, quando esses recursos são usados de forma errada, as mensagens de erro lançadas são úteis para orientar os usuários. Os recursos que mais me entusiasmam são os geradores e as strings de modelo. Se eu estivesse iniciando um novo projeto de hobby hoje, com certeza daria uma chance ao io.js, brincaria, ficaria louco e experimentaria esses recursos na natureza.

Toby Ho

Sobre Toby Ho

Toby é um desenvolvedor veterano de JavaScript. Ele gosta de ajudar os outros a aprender por meio de blogs, organizando encontros, dando workshops e ensinando em particular. Ele criou o executor de testes interativo Testem, que é seu trabalho de código aberto de maior orgulho até hoje.