JavaScript: Tudo sobre arrow functions
Apesar de ser uma feature bastante usada hoje em dia, algumas pessoas ainda não conhecem alguns segredos dessa nova sintaxe de funções em JavaScript.
Quer saber tudo o que as arrow functions são capazes de fazer? Vem comigo que eu te mostro =)
Nova sintaxe
Pra quem ainda não teve contato com as arrow functions, Vamos começar entendendo a nova sintaxe.
1 | const sum = (a, b) => { |
À primeira vista pode parecer estranho, mas usando function
tradicional, podemos obter o mesmo resultado assim:
1 | const sum = function (a, b) { |
Acho que assim fica mais fácil de entender =)
A criação de uma arrow function consistem em 3 “passos”:
- Os parênteses, que é por onde a função recebe os argumentos (assim como na
function
tradicional); - A “seta”
=>
- responsável pelo nome “arrow” function; - E as chaves: o bloco de código que representa o corpo da função.
Perceba que a ideia é bastante similar à forma padrão de usar funções em JS, com a diferença que não usamos a palavra chave function
, e sim uma seta (=>
) entre os parênteses (argumentos) e as chaves (bloco que gera o corpo da função).
Perceba também que nós usamos a arrow function como uma expressão - no caso, atribuindo o resultado da criação da arrow function à uma variável.
Diferente das function
tradicionais, não existe uma forma literal de escrever uma arrow- function. Ela sempre deve ser usada como uma expressão.
Também não é possível nomear uma arrow function. Elas são sempre anônimas.
E para executar a função, nada muda:
1 | console.log(sum(1, 2)) // 3 |
Variações da sintaxe
Arrow functions permitem algumas variações, para deixar o código ainda mais enxuto. Vamos ver alguns exemplos:
1) Função que não tem nada no seu corpo além do retorno
Se a função vai apenas retornar algum valor, nós podemos omitir as chaves (corpo da função) e a palavra chave return
.
Ao invés de escrever:
1 | const sum = (a, b) => { |
Podemos apenas escrever:
1 | const sum = (a, b) => |
O resultado é o mesmo! E não há problema algum em quebrar linha após a “seta”, pois diferente do return
, aqui não se aplica o ASI (Automatic Semicolon Insertion, ou Inserção Automática do Ponto-e-Vírgula).
Pra deixa ainda mais simples, podemos colocar tudo em uma linha só:
1 | const sum = (a, b) => a + b |
Para esse exemplo, o retorno da função fica implícito. Tudo o que está sendo colocado logo após a “seta” será considerado o retorno da função =)
ATENÇÃO
No exemplo acima, nós estamos retornando apenas a soma de dois números. Essa sintaxe é válida para qualquer tipo de dado sendo retornado exceto para objetos.
Por que objetos são uma exceção?
Para criarmos objetos literais, nós usamos as chaves. Logo, ao retornar um objeto, a criação da função teria um sentido ambíguo: as chaves são para abrir o bloco do corpo da função, ou é um retorno de objeto literal?
Para esse caso específico, sempre que quisermos retornar um objeto de forma implítica usando uma arrow function, nós precisamos envolver esse objeto em parênteses.
Vamos ver alguns exemplos.
Para retornar um objeto de forma explícita (com o corpo da função, e o return
), nós faríamos assim:
1 | const person = () => { |
Agora, se retornar de forma implícita, teríamos que fazer da seguinte forma:
1 | const person = () => ({ |
Veja que a diferença foi apenas envolver o objeto em parênteses.
Em JS, os parênteses servem apenas para dar prioridade na execução de alguma expressão, então basicamente o que acontece é que toda a expressão dentro dos parênteses é que será retornada pela função person
, assim que ela for executada. Fique muito atenta(o) a isso =)
2) Funções com apenas um argumento
Quando a função recebe apenas um argumento, podemos omitir os parênteses.
Então essa função:
1 | const plusOne = (value) => { |
Poderia ser escrita com retorno implícito:
1 | const plusOne = (value) => value + 1 |
E como só um argumento é esperado pela função, podemos omitir os parênteses:
1 | const plusOne = value => value + 1 |
Importante: Não é obrigatório omitir os parênteses. Eu, particularmente, prefiro deixar, pois acho que torna a leitura mais fácil. Mas é uma possibilidade. Com o tempo pode ser que você se acostume, então use da forma que achar melhor. Apenas siga um padrão do início ao fim do seu projeto =)
Posso substituir todas as “function” por “arrow function”?
Essa é uma ótima pergunta, e muitas pessoas não a fazem, apenas substituem e vida que segue.
Mas não é bem assim!
As arrow functions não foram criadas apenas para ser uma sintaxe mais exuta e substituir as function
tradicionais. Elas têm alguns detalhes importantes que é preciso levar em consideração antes de saber se devemos ou não só sair substituindo tudo.
Escopo léxico
Funções, ao serem criadas, geram um escopo próprio. E esse escopo só existe enquanto a função existe.
No JavaScript, temos a palavra chave this
, que é bastante polêmica, pois na maioria das linguagens, essa palavra chave faz referência à um objeto criado à partir de um construtor, quando usada dentro de uma classe; ou quando usada dentro de um método de objeto, ela faz referência à esse objeto.
Mas isso nem sempre é verdade: é possível “injetar” o this
, dependendo da forma que você executa uma função.
Se você não está por dentro desse assunto, recomendo esse post que escrevi a um tempo atrás. Leia o artigo, e depois volte aqui que tudo vai fazer sentido. Pode ir. Eu espero =)
…
Já leu? Ótimo, vamos continuar! :D
O importante aqui é saber que, diferente das function
tradicionais, você não consegue “injetar” o this
em arrow functions. Isso mesmo!
Se o this
for usado dentro de uma arrow function, esse this
vai fazer referência ao objeto que ele já era referência no momento da criação da arrow function.
Ficou complicado de entender? Vamos ver alguns exemplos!
1 | function getThis () { |
Se executarmos a função getThis
, criada no escopo global, em algum navegador, vamos ter como retorno o objeto window
, pois quando uma função que têm o this
dentro não for uma função construtora, nem um método de objeto, o this
vai ser referência ao objeto global.
Mas se usarmos 'use strict'
, como você já deve saber, essa diretiva faz com que o this
global seja undefined
. Logo, esse é o resultado quando estamos executando nossa função em strict mode:
1 |
|
E a questão interessante ao usar arrow function é exatamente essa: nos dois casos, a arrow function vai retornar o objeto window
, por causa do escopo léxico. Lembre-se: o this
dentro de uma arrow function sempre vai ser referência ao objeto que ele já era referência no momento da criação da arrow function. (nesse caso, o objeto window
):
1 | const getThis = () => { |
E usando 'use strict'
:
1 |
|
Uma outra situação bastante comum é ao usar um método de objeto:
1 | const counter = { |
Agora, ao tentar usar uma arrow function como método do objeto, olhe o que acontece:
1 | const counter = { |
Veja que o valor retornado pelo método increment
agora é NaN
. E isso acontece porque o objeto está sendo criado no escopo global, logo, o método increment
também está sendo criado nesse escopo, já que o objeto counter
não está dentro de nenhuma função.
E outra coisa importante a notar é o valor de value
:
1 | console.log(counter.value) // 0 |
Veja que o valor de value
não foi alterado! Logo, o value
que está sendo exibido é um valor que foi criado no objeto global window
, e para esse value
é que foi atribuído o valor NaN
:
1 | console.log(window.value) // NaN |
Com isso, fica claro que o this
dentro de uma arrow function vai ser sempre referência ao objeto ao qual ele já era, no momento em que a função foi criada.
Essa regra vale para qualquer chamada de função que precise usar o this
dentro, então preste atenção, ok? =)
Só mais um exemplo, apenas para complementar: eventos!
O método addEventListener
sempre injeta o this
dentro da função usada como listener desse evento.
Veja esse exemplo:
1 | const $input = document.querySelector('input[type="text"]') |
No exemplo acima, estou assumindo que o código vai ser executado em um ambiente onde existe um campo input
do tipo text
.
Ao começar a digitar algo dentro desse campo, podemos ver o valor do campo sendo exibido no console
, pois o this
dentro da função passada como listener do evento é injetado pelo addEventListener
, fazendo referência ao objeto do DOM ao qual o evento foi atrelado.
Agora tente usar uma arrow function no lugar da function
tradicional:
1 | const $input = document.querySelector('input[type="text"]') |
Nesse caso, veja que o valor exibido no cosole é sempre undefined
(a menos que exista um objeto no escopo em que a função foi criada, e esse objeto tenha uma propriedade value
).
Considerações
Com tudo isso em mente, podemos considerar alguns pontos:
- se a função que você tem não depende do valor de algum
this
, você pode substituí-la por arrow function sem problemas; - evite usar o
this
. No caso do exemplo do evento, toda função listener de um evento recebe um objeto de evento, com uma propriedadetarget
, que faz referência ao elemento que recebeu o evento. Use esse objeto se precisar manipular ou fazer qulquer coisa com o elemento que disparou o evento, ao invés de usarthis
. Dessa forma você evita os problemas vistos acima.
Parabéns! Agora você já sabe como usar corretamente as arrow functions sem problemas!
Já conhecia essa nova sintaxe de funções? Ficou com alguma dúvida? Faltou falar alguma coisa no post? Gostaria de sugerir um assunto para os próximos posts?
Deixe nos comentários! Ficarei muito feliz em ler e te responder :D
Até a próxima :D