Uma das inovaçÔes mais notĂĄveis ââdo JavaScript moderno Ă© o aparecimento de funçÔes de seta, que Ă s vezes sĂŁo chamadas de funçÔes de seta "gordas". Ao declarar essas funçÔes, eles usam uma combinação especial de caracteres -
=>
.
As funçÔes de seta tĂȘm duas vantagens principais sobre as funçÔes tradicionais. O primeiro Ă© uma sintaxe muito conveniente e compacta. A segunda Ă© que a abordagem para trabalhar com
this
nas funçÔes de seta parece mais intuitiva do que nas funçÔes comuns.
Ăs vezes, essas e outras vantagens levam ao fato de que a sintaxe da seta recebe uma preferĂȘncia incondicional sobre outras formas de declarar funçÔes. Por exemplo, a popular
configuração de eslint do Airbnb força que sempre que uma função anÎnima é criada, essa função seria do tipo flecha.
No entanto, como outros conceitos e mecanismos usados ââna programação, as funçÔes de seta tĂȘm seus prĂłs e contras. Seu uso pode causar efeitos colaterais negativos. Para usar as funçÔes de seta corretamente, vocĂȘ precisa conhecer os possĂveis problemas associados a elas.
O material, cuja tradução publicamos hoje, se concentrarå em como as funçÔes de seta funcionam. Aqui, consideraremos situaçÔes em que seu uso pode melhorar o código e situaçÔes em que eles não devem ser usados.
Apresenta funçÔes de seta em JavaScript
As funçÔes de seta no JavaScript são algo como
funçÔes lambda em Python e
blocos em Ruby.
Essas sĂŁo funçÔes anĂŽnimas com uma sintaxe especial que recebem um nĂșmero fixo de argumentos e funcionam no contexto do escopo que os inclui, ou seja, no contexto da função ou outro cĂłdigo no qual eles sĂŁo declarados.
Vamos falar sobre isso com mais detalhes.
Arrow FunçÔes de seta de sintaxe
As funçÔes de seta sĂŁo construĂdas de acordo com um Ășnico esquema, enquanto a estrutura de funçÔes pode ser, em casos especiais, simplificada. A estrutura bĂĄsica da função de seta Ă© assim:
(argument1, argument2, ... argumentN) => {
A lista de argumentos da função estĂĄ entre parĂȘnteses, seguida por uma seta composta de caracteres
=
e
>
e, em seguida, vem o corpo da função entre chaves.
Isso é muito semelhante ao funcionamento das funçÔes comuns, as principais diferenças são que a palavra-chave
function
Ă© omitida aqui e uma seta Ă© adicionada apĂłs a lista de argumentos.
Em certos casos, no entanto, funçÔes simples de seta podem ser declaradas usando construçÔes muito mais compactas.
Considere a sintaxe usada se o corpo da função for representado por uma Ășnica expressĂŁo. Ele permite que vocĂȘ fique sem colchetes que enquadram o corpo da função e elimina a necessidade de retornar explicitamente os resultados da avaliação de uma expressĂŁo, pois esse resultado serĂĄ retornado automaticamente. Por exemplo, pode ser assim:
const add = (a, b) => a + b;
Aqui estå outra variante da notação abreviada da função, usada quando a função possui apenas um argumento.
const getFirst = array => array[0];
Como vocĂȘ pode ver, os parĂȘnteses que cercam a lista de argumentos sĂŁo omitidos aqui. AlĂ©m disso, o corpo da função, que neste exemplo Ă© representado por um Ășnico comando, tambĂ©m Ă© gravado sem colchetes. Mais tarde, falaremos mais sobre os benefĂcios de tais projetos.
Objetos de retorno e funçÔes de seta de registro curto
Ao trabalhar com funçÔes de seta, tambĂ©m sĂŁo usadas algumas construçÔes de sintaxe mais complexas, Ășteis para conhecer.
Por exemplo, tente usar uma expressĂŁo de linha Ășnica para retornar de uma função literal do objeto. Pode parecer, dado o que jĂĄ sabemos sobre as funçÔes de seta, que uma declaração de função serĂĄ semelhante a esta:
(name, description) => {name: name, description: description};
O problema com este código é sua ambiguidade. Ou seja, as chaves que queremos usar para descrever o objeto literal parecem estar tentando incluir o corpo de uma função nelas.
Para indicar ao sistema que queremos dizer o objeto literal, precisamos incluĂ-lo entre parĂȘnteses:
(name, description) => ({name: name, description: description});
â FunçÔes de seta e seu contexto de execução
Diferentemente de outras funçÔes, as funçÔes de seta nĂŁo tĂȘm seu prĂłprio
contexto de execução .
Na prĂĄtica, isso significa que eles herdam as entidades
this
e
arguments
da função pai.
Por exemplo, compare as duas funçÔes apresentadas no código a seguir. Um deles é comum, o segundo é flecha.
const test = { name: 'test object', createAnonFunction: function() { return function() { console.log(this.name); console.log(arguments); }; }, createArrowFunction: function() { return () => { console.log(this.name); console.log(arguments); }; } };
HĂĄ um objeto de
test
com dois métodos. Cada um deles é uma função que cria e retorna uma função anÎnima. A diferença entre esses métodos é apenas que, no primeiro deles, a expressão funcional tradicional é usada e na função da segunda seta.
Se experimentarmos esse código no console, passando os mesmos argumentos para os métodos do objeto, embora os métodos pareçam muito semelhantes, obteremos resultados diferentes:
> const anon = test.createAnonFunction('hello', 'world'); > const arrow = test.createArrowFunction('hello', 'world'); > anon(); undefined {} > arrow(); test object { '0': 'hello', '1': 'world' }
Uma função anĂŽnima tem seu prĂłprio contexto, portanto, quando for chamada, quando vocĂȘ chamar
test.name
, o valor da propriedade
name
do objeto nĂŁo serĂĄ
test.name
e, quando vocĂȘ chamar
arguments
, a lista de argumentos da função usada para criar e retornar a função sob investigação não serå exibida.
No caso de uma função de seta, verifica-se que seu contexto coincide com o contexto da função que a criou, o que lhe då acesso tanto à lista de argumentos transmitidos por essa função quanto à propriedade de
name
do objeto cujo método é essa função.
SituaçÔes em que as funçÔes de seta aprimoram o código
Processando listas de valores
FunçÔes lambda tradicionais, bem como funçÔes de seta, depois que aparecem em JavaScript, geralmente são usadas em situaçÔes em que uma determinada função é aplicada a cada elemento de uma determinada lista.
Por exemplo, se houver uma matriz de valores que precise ser convertida usando o método matrizes de
map
, uma função de seta é ideal para descrever essa conversão:
const words = ['hello', 'WORLD', 'Whatever']; const downcasedWords = words.map(word => word.toLowerCase());
Aqui estå um exemplo extremamente comum do uso semelhante de funçÔes de seta, que consiste em trabalhar com as propriedades dos objetos:
const names = objects.map(object => object.name);
Da mesma forma, se em vez de tradicionais
for
loops, eles usam modernos loops
forEach
baseados em
forEach
, as funçÔes de seta usam
this
entidade pai, tornam seu uso intuitivo:
this.examples.forEach(example => { this.runExample(example); });
â Promessas e cadeias de promessas
Outra situação em que as funçÔes de seta permitem escrever cĂłdigos mais limpos e compreensĂveis Ă© representada por construçÔes de software assĂncronas.
Portanto, as
promessas simplificam bastante o trabalho com cĂłdigo assĂncrono. Ao mesmo tempo, mesmo se vocĂȘ preferir usar a
construção assĂncrona / aguardada
, vocĂȘ nĂŁo pode prescindir
das promessas , pois essa construção é baseada nelas.
No entanto, ao usar promessas, vocĂȘ precisa declarar funçÔes chamadas apĂłs a conclusĂŁo do cĂłdigo assĂncrono ou a conclusĂŁo da chamada assĂncrona para uma determinada API.
Este é o local ideal para usar as funçÔes de seta, especialmente se a função resultante tiver um determinado estado, se referir a algo no objeto. Por exemplo, pode ser assim:
this.doSomethingAsync().then((result) => { this.storeResult(result); });
â Transformação de Objetos
Outro caso de uso comum para funçÔes de seta é encapsular transformaçÔes de objeto.
Por exemplo, no Vue.js, existe um
padrĂŁo comum
para incluir fragmentos de armazenamento do Vuex diretamente em um componente do Vue usando o
mapState
.
Esta operação inclui declaraçÔes de um conjunto de "conversores" que selecionam exatamente o que Ă© necessĂĄrio para um componente especĂfico a partir do estado completo inicial.
Essas transformaçÔes simples são o lugar perfeito para usar as funçÔes de seta. Por exemplo:
export default { computed: { ...mapState({ results: state => state.results, users: state => state.users, }); } }
SituaçÔes em que as funçÔes de seta não devem ser usadas
â MĂ©todos de objeto
Existem vårias situaçÔes em que o uso das funçÔes de seta não é uma boa ideia. As funçÔes de seta, se usadas de ùnimo leve, não apenas não ajudam os programadores, mas também se tornam uma fonte de problemas.
A primeira situação é usar funçÔes de seta como métodos de objeto. O contexto de execução e a
this
, especĂficos para funçÔes tradicionais, sĂŁo importantes aqui.
Ao mesmo tempo, era popular usar uma combinação de propriedades de classe e funçÔes de seta para criar mĂ©todos com "ligação automĂĄtica", ou seja, aqueles que podem ser usados ââpor manipuladores de eventos, mas permanecem vinculados Ă classe. Parecia algo assim:
class Counter { counter = 0; handleClick = () => { this.counter++; } }
Usando uma construção semelhante, mesmo que a função
handleClick
chamada pelo manipulador de eventos e nĂŁo no contexto de uma instĂąncia da classe
Counter
, essa função teve acesso aos dados dessa instùncia.
No entanto, essa abordagem tem muitos pontos negativos aos quais
esse material Ă© dedicado.
Embora o uso de uma função de seta aqui, é claro, seja uma maneira conveniente de vincular uma função, o comportamento dessa função em muitos aspectos estå longe de ser intuitivo, interferindo no teste e criando problemas em situaçÔes em que, por exemplo, eles tentam usar o objeto correspondente como um protótipo.
Nesses casos, em vez de funçÔes de seta, use funçÔes comuns e, se necessårio, vincule a elas uma instùncia do objeto no construtor:
class Counter constructor() }
ChainsLonga cadeia de chamadas
As funçÔes de seta podem se tornar uma fonte de problemas se forem planejadas para serem usadas em muitas combinaçÔes diferentes, em particular, em longas cadeias de chamadas de função.
A principal razão para esses problemas, como ao usar funçÔes anÎnimas, é que eles fornecem resultados extremamente pouco informativos do rastreamento da
pilha de chamadas .
Isso nĂŁo Ă© tĂŁo ruim se, por exemplo, houver apenas um nĂvel de aninhamento de chamadas de função, por exemplo, se estivermos falando sobre a função usada no iterador. No entanto, se todas as funçÔes usadas forem declaradas como seta, e essas funçÔes se chamarem, se ocorrer um erro, nĂŁo serĂĄ fĂĄcil descobrir o que estĂĄ acontecendo. As mensagens de erro terĂŁo a seguinte aparĂȘncia:
{anonymous}() {anonymous}() {anonymous}() {anonymous}() {anonymous}()
With FunçÔes com contexto dinùmico
A Ășltima das situaçÔes em que estamos discutindo, nas quais as funçÔes de seta podem se tornar uma fonte de problemas, Ă© usĂĄ-las onde vocĂȘ precisa de uma ligação dinĂąmica.
Se funçÔes de seta forem usadas nessas situaçÔes, a dinùmica
this
ligação não funcionarå. Essa surpresa desagradåvel pode levar a que se confundam as razÔes do que acontece com aqueles que precisam trabalhar com código no qual as funçÔes das setas são usadas incorretamente.
Aqui estão algumas coisas a serem lembradas ao considerar o uso das funçÔes de seta:
- Manipuladores de eventos sĂŁo chamados com
this
associado ao atributo de evento currentTarget
. - Se vocĂȘ ainda estiver usando jQuery, considere que a maioria dos mĂ©todos jQuery vincula
this
ao elemento DOM selecionado. - Se vocĂȘ usa o Vue.js, mĂ©todos e funçÔes calculadas geralmente o vinculam ao componente Vue.
Obviamente, as funçÔes de seta podem ser usadas intencionalmente para alterar o comportamento padrão dos mecanismos de software. Mas, especialmente nos casos com jQuery e Vue, isso geralmente entra em conflito com o funcionamento normal do sistema, o que leva ao fato de que o programador não consegue entender por que algum código que parece completamente normal se recusa a trabalhar repentinamente.
SumĂĄrio
As funçÔes de seta sĂŁo um recurso JavaScript maravilhoso e fresco. Eles permitem, em muitas situaçÔes, escrever cĂłdigo mais conveniente do que antes. Mas, como Ă© o caso de outros recursos, eles tĂȘm vantagens e desvantagens. Portanto, vocĂȘ precisa usar as funçÔes de seta onde elas podem ser Ășteis, sem considerĂĄ-las como um substituto completo para funçÔes comuns.
Caros leitores! VocĂȘ encontrou situaçÔes nas quais o uso das funçÔes de seta leva a erros, inconvenientes ou comportamento inesperado dos programas?
