JavaScript Pill — Arrow Functions

Gabriel Rafael
sysvale
Published in
6 min readAug 14, 2018

Saudações!

Neste fascículo iremos tratar das Arrow Funcions, quem são, onde vivem, pra que servem e para o que não servem…

Hoje no… JS Pill

As Arrows Functions são uma das features mais populares introduzidas na especificação ECMAScript® 2015, e trouxeram consigo algumas características que gostaria de elencar antes de partirmos para a exemplificação. Neste primeiro momento iremos entender alguns conceitos e em seguida falaremos sobre a sintaxe.

2ality.com
ponyfoo.com

Ps.: Neste artigo, “expressões” são termos que produzem valor quando resolvidos e “declarações” são termos que “fazem coisas”.

Conceitos

  1. Arrow Functions são sempre anônimas;
  2. O this externo a função não é lexicalmente ocultado;
  3. Arrow Functions não surgiram com o intuito de substituir as funções tradicionais;
  4. Pense nelas como um novo tipo de função que não oculta o this. Mas não enxergue-as como simples funções com “binding integrado”.

Discussão

  1. Quando uma função é dita anônima, significa dizer que a função não pode ser nomeada. Isso faz com que algumas peculiaridades venham à tona, como por exemplo, o debug se torna mais difícil porque não se sabe ao certo onde o erro ocorreu (linha específica); um outro exemplo é que a autorreferência não irá funcionar (recursão, event handler unbind, …).
  2. Isso significa dizer que as Arrows Functions não possuem seu próprio this, como as funções tradicionais do JS. O contexto this interno a função é determinado pelo contexto de execução. Ou seja, o this da arrow function é o primeiro this externo à ela, seja ele o global ou o de outra função tradicional qualquer. Desta forma não é necessário fazer o bind do this ou o that = this que conhecemos. 😅
  3. Como já foi dito anteriormente, há casos em que as Arrows Functions não são aplicáveis, a exemplo de event handlers, métodos de objetos, funções que trabalhem com arguments e funções de prototype, veremos em breve exemplos desses casos.
  4. Isso recai nos tópicos anteriores, onde foi dito que as Arrows Functions não surgiram pra substituir as funções tradicionais. São uma nova feature que abrem novas oportunidades para os desenvolvedores.

Sintaxe

A “seta gorda” => (do inglês fat arrow) é a simbologia utilizada para representar as Arrow Functions (daí seu nome).

Não confunda com menor ou igual à <= .

Além da seta, a declaração da função segue o modelo abaixo:

(param1, param2, ...) => { ... }

onde em casos específicos pode sofrer algumas alterações;

  • Arrow function sem parâmetros: Os parênteses são necessários.
() => { ... }
  • Quando há somente um identificador, pode-se omitir os parênteses, porém, caso esse identificador possua um valor default, os parênteses devem estar presentes. Caso seja um único parâmetro sem identificador, os parênteses também devem existir.
param1 => { ... }
(param1 = 0) => { ... }
([1, 2]) => { ... }
  • Se o corpo da função for composto por uma única expressão, as chaves podem ser omitidas.
(a, b) => a * b;
  • Se o corpo da função for composto por uma única expressão de retorno, a palavra chave return pode ser omitida.
(a, b) => return a * b; // é equivalente a
(a, b) => a * b;

ou seja, sempre que houver uma única expressão no bloco do corpo da função, o valor da expressão resolvida será retornado.

  • Se uma expressão for o corpo da função, as chaves podem ser omitidas.
x => console.log(x);
  • Se o retorno da função for um objeto literal, é necessário encapsular o retorno entre parênteses.
x => ({ y: x })

Isso ocorre porque o código dentro das chaves é “parseado” como um conjunto de declarações, por isso os parênteses são necessários.

Quebra de linha

É possível quebrar a linha na declaração da função, mas somente após a seta, ou entre os parâmetros. Observe os casos possíveis:

Peculiaridades

São muitas as características dessas funções, espero não passar uma imagem negativa das mesmas devido as suas restrições. Mas o intuito aqui é informar para evitar futuras possíveis dores de cabeça. Usem-as sempre que puderem! Em minha opinião, o código fica mais conciso e belo.

In love with arrows
  • Se formos pensar na seta ( => ) como um operador, poderíamos dizer que ele possui baixa precedência, de modo que se houver um conflito com outros operadores, eles irão vencer e isso irá manter os parâmetros e o corpo da função “juntos”.
const func = x => (x + 1) > 10 ? x : 0; // é equivalente a
const func = x => ((x + 1) > 10 ? x : 0);

Caso você queira que o operador “seta gorda” dispute com outros operadores, deve-se utilizar parenteses.

const func1 = (x => (x + 1)) > 10 ? x : 0; // => vencem > e ?const func2 = (x => ((x + 1) > 10)) ? x : 0; // => vence ? e perde para >
  • Devido ao fato do contexto interno as Arrow Functions ser definido pelo contexto circundante, o uso do 'use strict' não surte qualquer efeito nesse tipo de função.
  • As Arrow Functions podem ser imediatamente auto-invocadas, assim como as funções tradicionais.
(() => {
return "Hello World';
})();
  • Arrow Functions não possuem seu próprio objeto arguments . Então, se você escrever uma função que faça uso de tal objeto, receberá um erro de não definição do mesmo. Como alternativa, você pode utilizar o spread operator como argumento ao chamar a função e então acessá-lo como um array.
const foo = (...arguments) => arguments[0]; 

console.log(foo(1, 2, 3, 4)); // 1
  • Arrow Functions não possuem o objeto prototype .
var Foo = () => {};
console.log(Foo.prototype); // undefined

Onde não usar

A intenção aqui é detalhar um pouco mais alguns casos mencionados acima e de forma semelhante à seção anterior, também evitar futuras dores de cabeça.

  • Como dito previamente, Arrow Functions se aplicam melhor a funções não-métodos. Veja o que ocorre ao utilizá-las como método de um objeto:
let objeto = {
a: 10,
b: () => console.log(this.a, this),
c: function() {
console.log(this.a, this);
}
}

objeto.b(); // undefined, Window(ou o objeto global)
objeto.c(); // 10, Object
  • No caso do de usá-la como construtor de classe, temos algo como o exemplo abaixo.
let Test = () => {};
let test = new Test(); // TypeError: Test is not a constructor
  • Em Handlers de eventos também não é ideal aplicá-las. Imagine o seguinte cenário, temos um botão, que ao clicarmos nele, sua cor de background é alterada através da adição de uma classe de estilo, veja o código:
const button = document.querySelector('#button');button.addEventListener('click', () => {
this.classList.toggle('blue');
});

Isso gerará o seguinte erro,

TypeError, cannot read property 'toggle' of undefined

devido ao fato do this interno ao event listener ser o this do objeto global e não o this do evento.

  • Um último caso a ser levado em consideração é que essa sintaxe pode não ser de fácil entendimento para todos os membros de sua equipe. Quando escrevemos funções tradicionais as pessoas sabem o que esperar delas, e, as vezes, Arrow Functions podem ser um tanto difíceis de se ler.

Conclusão

Arrow Functions são muito úteis se é compreendido o seu escopo de aplicação, tornam o código mais enxuto e elegante quando bem empregadas. Entretanto, alguns cuidados precisam ser tomados ao utilizá-las (como qualquer coisa, diga-se de passagem), até mesmo em relação à receptividade e nível de compreensão da equipe.

Até breve!

--

--