
Selon certains sondages , les fonctions de flèche anonyme en JavaScript sont la caractéristique la plus populaire d'ES-2015, qui est également soulignée par le nombre complet de tutoriels sur Internet. Ils sont indéniablement très utiles, mais dans ce court article, nous examinerons des exemples d'utilisation privée d'expressions au moins merveilleuses avec des fonctions nommées - NFE.
Aide courte
Expression de fonction nommée - une extension d'expressions fonctionnelles en JavaScript qui vous permet de nommer une fonction créée dans le cadre d'une expression ( FunctionExpression ):
let fe = function named(...) { };
L'intérêt est qu'à l'intérieur de la fonction référencée par la variable fe , il y a accès à la fonction elle-même via le nom nommé . Il est impossible de réécrire le nom uniquement depuis la fonction!
Comment l'utiliser
Par exemple, nous devons écrire une fonction de décoration qui comptera le nombre d'appels à une fonction. Cela peut être fait avec élégance avec NFE:
const count = f => function df() { df.calls = (df.calls || 0) + 1; return f(...arguments); };
Ici, nous avons eu accès à la fonction df retournée, de sorte que lorsqu'elle est appelée, nous enregistrons le compteur dans la propriété calls , qui sera disponible en lecture si nécessaire:
const csum = count((x, y) => x + y); csum(5, 10);
Ou enregistrez tous les résultats de l'appel de fonction:
const accum = f => function df() { let res = f(...arguments); (df.results = (df.results || [])).push(res); return res; };
Dans tous les cas, nous profitons du fait qu'une fonction en JavaScript est un objet, et nous pouvons le compléter avec les propriétés nécessaires dans nos tâches locales.
Évidemment, nous pouvons appeler une fonction. L'utilisation de NFE pour la récursivité est particulièrement agréable. Supposons que nous voulons trouver le nième membre de la séquence de Fibonacci (pour une raison quelconque, tout le monde le veut tôt ou tard):
const rf = function getfn(n) { return n > 2 ? getfn(n - 2) + getfn(n - 1) : 1; }; rf(1);
Il ne vaut pas la peine de prêter attention à la "queue" ici. Mais sur la possibilité de faire de même via FunctionDeclaration - oui. Mais la fonction d'expression a un avantage: si vous voulez transférer la fonction de rf vers une autre variable, vous n'avez pas besoin de modifier l'appel récursif à l'intérieur.
Un bon exemple d'implémentation d'une minuterie:
const ping = (callback, t) => setTimeout(function pf() { callback(); setTimeout(pf, t); }, t);
Ici, nous utilisons NFE comme littéral comme argument au lieu de déclarer une variable inutile. Pourquoi ne pas définir Intervalle ? Au lieu d'un rappel, ici, il peut y avoir une attente pour une résolution de la promesse avant le prochain tic du minuteur.
Un exemple intéressant serait la combinaison de NFE et IIFE ( Immediately-Invoked Function Expression ), quand en place seul le résultat de la fonction récursive est nécessaire. Une seule fois:
let data = { f10: function fact(n) { return n > 1 ? n * fact(n - 1) : 1; }(10) }; data.f10;
En est-il ainsi? eh bien, voici le vrai problème: il y a une fonction strictement croissante f qui agit dans l'ensemble des nombres naturels. Trouvez le point extrême x auquel la valeur de la fonction ne dépasse pas le y donné. Un exemple d'une telle fonction serait 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);
Nous n'entrerons pas dans les détails mathématiques. Considérez la mise en œuvre:
- La fonction de recherche requise a deux de nos paramètres f, y et renvoie le résultat de IIFE.
- Une fonction appelée instantanément implémente une recherche binaire d'une solution, le premier appel obtient la plage initiale.
- La recherche elle-même est implémentée par récursivité, qui utilise le nom NFE pour les appels suivants. Nous ne déclarons pas de fonction de recherche et ne créons pas de nouvelle variable.
Bien sûr, un problème similaire peut être résolu par un cycle simple avec une condition préalable, mais cela est trop impératif pour le codage de la console.
Enfin, quelques mots sur la trace de la pile. Nous avons une fonction NFE, dans le corps de laquelle une exception a été levée:
const fe = function named() { throw new Error('Something went wrong'); };
Puisque nous avons une fonction nommée dans l'expression, nous la verrons dans la trace de la pile:
Uncaught Error: Something went wrong at named (<anonymous>:2:11)
Cependant, à partir d'ES-2015, de nombreuses expressions de fonction anonymes créent en fait une fonction avec un nom, la sortant de son contexte:
const unnamed = function() { throw new Error('Something went wrong'); };
La fonction du côté droit de l'expression est associée à la variable de gauche:
Uncaught Error: Something went wrong at unnamed (<anonymous>:2:7)
Mais ce n'est pas toujours possible. Un exemple classique est l'initialisation d'un script de bibliothèque externe via IIFE, en option:
;(function() {
Ou un exemple fréquent d'une fonction renvoyant une autre fonction:
const bind = (f, ctx) => function() { return f.apply(ctx, arguments); };
Il n'y a pas de variable pour la sortie, donc en cas d'exception nous verrons anonyme . L'ENF peut aider un peu dans les litiges.
Conclusion courte
NFE est bon pour écrire du code concis et non pris en charge pour prototyper rapidement un large éventail de tâches. Pour utiliser leur beauté dans le code final, vous devriez penser à plusieurs reprises: JavaScript est déjà surchargé de spécifications intéressantes.