ECMAScript 2015: Arrow Functions

ES2015 Gotchas é uma série quinzenal sobre as novidades e pegadinhas do ES2015.

Essa é provavelmente uma das features mais famosas do ES2015, e ao mesmo tempo deve ser a que gera mais confusão e surpresas. De início parece apenas uma melhoria estética para as tradicionais e verbosas funções do javascript, que incomodam bastante programadores que vem de outras linguagens de script, mas não é bem assim, elas têm objetivos e funcionalidades diferentes.

Sem contexto de execução

Toda vez que uma função tradicional do JavaScript é chamada, seu contexto é criado antes de sua execução, nesse momento é determinado o valor das palavras reservadas this e arguments. Esse processo simplesmente não acontece com as novas arrow functions, elas são executadas sobre exatamente o mesmo contexto de onde forem criadas, utilizando o mesmo this e do arguments desse contexto, como se fossem qualquer outra variável externa a ela que estivesse acessando, tornando impossível alterá-los de qualquer forma.

Como se isso não fosse uma pegadinha por si só, nem sempre está claro qual o contexto sobre o qual a arrow function está sendo criada. Vejamos o exemplo abaixo:

let foo = {  
  x: 666,
  normal: function(){ console.log(this.x, arguments) },
  arrow: () => console.log(this.x, arguments)
}

foo.normal(1, 2) // 666, { '0': 1, '1': 2 }  
foo.normal.bind({x: 69})(2, 1) // 69, { '0': 2, '1': 1 }  
foo.normal.call({x: 69}, 2, 1) // 69, { '0': 2, '1': 1 }

foo.arrow(1, 2) // undefined, undefined  
foo.arrow.bind({x: 69})(2, 1) // undefined, undefined  
foo.arrow.call({x: 69}, 2, 1) // undefined, undefined  

Confuso? No momento da criação de nossa arrow function a declaração do objeto foo ainda não tinha sido terminada, então seu contexto ainda não existia, sendo assim nossa arrow function estará conectada ao contexto de execução diretamente acima, provavelmente o objeto global de execução do JavaScript, que não terá o valor de x ou arguments.

Lembrando que caso o exemplo acima tivesse feito uso de uma função construtura, nós teríamos um contexto no momento de criação da arrow function, e então o valor de x e arguments estariam eternamente conectados a ele, e não ao escopo global.

Arrow functions não são construtures

Toda função tradicional do JavaScript é também um construtor e podem ser instanciadas, para isso guardam seu próprio protótipo no atributo .prototype, que é inexistente nas novas arrow functions, graças a isso elas não podem ser usadas dessa forma e qualquer tentativa de fazer isso retornará um erro de execução.

let foo = {  
  normal: function(){ console.log(this.x, arguments) },
  arrow: () => console.log(this.x, arguments)
}

console.log(foo.normal.prototype); // {}  
console.log(new foo.normal); // {}

console.log(foo.arrow.prototype); // undefined  
console.log(new foo.arrow);  
// TypeError: () => is not a constructor

Sério, então para que elas servem?

A falta de vários recursos, contexto próprio, protótipo e etc, podem parecer estranhos mas têm suas vantagens, afinal grande parte das vezes não precisamos de tudo isso, então elas não serão apenas mais fáceis de ler e escrever, como utilizarão menos memória e terão sua performance de execução otimizadas. Elas foram feitas para casos de uso simples, casam perfeitamente com as funções de map, reduce, filter e outros tantos métodos funcionais já existentes no antigo ECMAScript 5!

Até a próxima! Fiquem ligados na série ES2015 Gotchas!

William Grasel

Read more posts by this author.