A beleza das funções NÃO anônimas em JavaScript

imagem


De acordo com algumas pesquisas , as funções de seta anônima no JavaScript são o recurso mais popular do ES-2015, o que também é destacado pelo amplo número de tutoriais na Internet. Eles são inegavelmente muito úteis, mas neste breve artigo veremos exemplos de como usar a atenção privada, pelo menos, expressões maravilhosas com funções nomeadas - NFE.


Ajuda curta


Expressão de função nomeada - uma extensão de expressões funcionais em JavaScript que permite nomear uma função criada como parte de uma expressão ( FunctionExpression ):


let fe = function named(...) { /* function body */ }; 

O ponto principal é que, dentro da função referenciada pela variável fe , há acesso à própria função através do nome nomeado . É impossível reescrever o nome apenas de dentro da função!


Como usá-lo


Por exemplo, precisamos escrever uma função de decoração que conte o número de chamadas para alguma função. Isso pode ser feito de maneira bastante elegante com a NFE:


 const count = f => function df() { df.calls = (df.calls || 0) + 1; return f(...arguments); }; 

Aqui temos acesso à função df retornada, para que, quando for chamada, salve o contador na propriedade calls , que estará disponível para leitura, se necessário:


 const csum = count((x, y) => x + y); csum(5, 10); // 15 csum(50, 1); // 51 csum.calls; // 2 

Ou salve todos os resultados da chamada de função:


 const accum = f => function df() { let res = f(...arguments); (df.results = (df.results || [])).push(res); return res; }; 

De qualquer forma, aproveitamos o fato de que uma função em JavaScript é um objeto e podemos complementá-la com as propriedades necessárias em nossas tarefas locais.


Obviamente, podemos chamar uma função. Usar a NFE para recursão é especialmente bom. Suponha que desejamos encontrar o enésimo membro da sequência de Fibonacci (por algum motivo, todo mundo quer isso mais cedo ou mais tarde):


 const rf = function getfn(n) { return n > 2 ? getfn(n - 2) + getfn(n - 1) : 1; }; rf(1); // 1 rf(10); // 55 

Não vale a pena prestar atenção ao "tailing" aqui. Mas na capacidade de fazer o mesmo através da FunctionDeclaration - sim. Mas a função de expressão tem uma vantagem: se você deseja transferir a função de rf para outra variável, não precisa editar a chamada recursiva interna.


Um exemplo puro de uma implementação de timer:


 const ping = (callback, t) => setTimeout(function pf() { callback(); setTimeout(pf, t); }, t); 

Aqui usamos NFE como literal como argumento, em vez de declarar uma variável desnecessária. Por que não setInterval ? Em vez de um retorno de chamada, aqui pode haver uma espera por uma resolução da promessa antes do próximo tique do cronômetro.


Um exemplo interessante seria a combinação de NFE e IIFE ( Expressão de Função Imediatamente Invocada ), quando em vigor apenas o resultado da função recursiva é necessário. Apenas uma vez:


 let data = { f10: function fact(n) { return n > 1 ? n * fact(n - 1) : 1; }(10) }; data.f10; // 3628800 

É isso mesmo? bem, aqui está o verdadeiro problema: existe alguma função estritamente crescente f que atua no conjunto de números naturais. Encontre o ponto extremo x no qual o valor da função não excede o y fornecido. Um exemplo dessa função seria f(x) = 3 * x + 5 ou f(x) = 2^x + 11 .


 const find = (f, y) => function bs(a, b) { if (a + 1 === b) return a; let m = Math.ceil((a + b) / 2); return f(m) <= y ? bs(m, b) : bs(a, m); }(-1, y + 1); find(x => 3 * x + 5, 200); // 65 find(x => Math.pow(2, x) + 11, 1000); // 9 

Não entraremos em detalhes matemáticos. Considere a implementação:


  1. A função de busca necessária possui dois de nossos parâmetros f, y e retorna o resultado de IIFE.
  2. Uma função chamada instantaneamente implementa uma pesquisa binária por uma solução; a primeira chamada obtém o intervalo inicial.
  3. A pesquisa em si é implementada por meio de recursão, que usa o nome NFE para chamadas subseqüentes. Não declaramos uma função de pesquisa e não criamos uma nova variável.

Obviamente, um problema semelhante pode ser resolvido por meio de um ciclo simples com uma pré-condição, mas isso é imperativo demais para a codificação do console.


Finalmente, algumas palavras sobre o rastreamento de pilha. Temos alguma função NFE, no corpo da qual uma exceção foi lançada:


 const fe = function named() { throw new Error('Something went wrong'); }; 

Como temos uma função nomeada na expressão, a veremos no rastreamento da pilha:


 Uncaught Error: Something went wrong at named (<anonymous>:2:11) 

No entanto, a partir do ES-2015, muitas expressões de função anônimas realmente criam uma função com um nome, tirando-a do contexto:


 const unnamed = function() { throw new Error('Something went wrong'); }; 

A função no lado direito da expressão está associada à variável à esquerda:


 Uncaught Error: Something went wrong at unnamed (<anonymous>:2:7) 

Mas isso nem sempre é possível. Um exemplo clássico é a inicialização de um script de biblioteca externa através do IIFE, como uma opção:


 /** * @license * @description */ ;(function() { // some awesome code }.call(this)); 

Ou um exemplo frequente de uma função retornando outra função:


 const bind = (f, ctx) => function() { return f.apply(ctx, arguments); }; 

Não há variável para saída, portanto, no caso de uma exceção, veremos o anônimo . A NFE pode ajudar um pouco em litígios.


Breve conclusão


A NFE é boa em escrever códigos concisos e sem suporte para prototipar rapidamente uma ampla variedade de tarefas. Para usar sua beleza no código final, pense duas vezes: o JavaScript já está sobrecarregado com especificações interessantes.

Source: https://habr.com/ru/post/pt427461/


All Articles