Die Schönheit NICHT anonymer Funktionen in JavaScript

Bild


Laut einigen Umfragen sind anonyme Pfeilfunktionen in JavaScript das beliebteste Merkmal von ES-2015, was auch durch die umfassende Anzahl von Tutorials im Internet unterstrichen wird. Sie sind zweifellos sehr nützlich, aber in diesem kurzen Artikel werden wir Beispiele für die Verwendung von zumindest wunderbaren Ausdrücken mit benannten Funktionen - NFE - betrachten, denen die Aufmerksamkeit entzogen ist.


Kurze Hilfe


Benannter Funktionsausdruck - eine Erweiterung von Funktionsausdrücken in JavaScript, mit der Sie eine Funktion benennen können, die als Teil eines Ausdrucks erstellt wurde ( FunctionExpression ):


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

Der springende Punkt ist, dass innerhalb der Funktion, auf die die Variable fe verweist, über den Namen auf die Funktion selbst zugegriffen werden kann. Es ist unmöglich, den Namen nur von innen umzuschreiben!


Wie man es benutzt


Zum Beispiel müssen wir eine Dekorationsfunktion schreiben, die die Anzahl der Aufrufe einer Funktion zählt. Dies kann mit NFE ganz elegant erfolgen:


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

Hier haben wir Zugriff auf die zurückgegebene df- Funktion, so dass wir beim Aufruf den Zähler in der Call- Eigenschaft speichern, die bei Bedarf zum Lesen zur Verfügung steht:


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

Oder speichern Sie alle Ergebnisse des Funktionsaufrufs:


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

In jedem Fall nutzen wir die Tatsache, dass eine Funktion in JavaScript ein Objekt ist, und können sie in unseren lokalen Aufgaben mit den erforderlichen Eigenschaften ergänzen.


Natürlich können wir eine Funktion aufrufen. Die Verwendung von NFE für die Rekursion ist besonders hilfreich. Angenommen, wir möchten das n-te Mitglied der Fibonacci-Sequenz finden (aus irgendeinem Grund möchte dies früher oder später jeder):


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

Es lohnt sich nicht, hier auf "Tailing" zu achten. Aber über die Fähigkeit, dasselbe durch FunctionDeclaration zu tun - ja. Die Ausdrucksfunktion hat jedoch einen Vorteil: Wenn Sie die Funktion von rf auf eine andere Variable übertragen möchten, müssen Sie den darin enthaltenen rekursiven Aufruf nicht bearbeiten.


Ein gutes Beispiel für eine Timer-Implementierung:


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

Hier verwenden wir NFE als Literal als Argument, anstatt eine unnötige Variable zu deklarieren. Warum nicht setInterval ? Anstelle eines Rückrufs kann hier vor dem nächsten Tick des Timers auf eine Lösung des Versprechens gewartet werden.


Ein interessantes Beispiel ist die Kombination von NFE und IIFE ( Sofort aufgerufener Funktionsausdruck ), wenn nur das Ergebnis der rekursiven Funktion benötigt wird. Nur einmal:


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

Ist das so Nun, hier ist das eigentliche Problem: Es gibt eine streng zunehmende Funktion f , die in der Menge der natürlichen Zahlen wirkt. Finden Sie den Extrempunkt x, an dem der Wert der Funktion das angegebene y nicht überschreitet. Ein Beispiel für eine solche Funktion wäre f(x) = 3 * x + 5 oder 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 

Wir werden nicht auf mathematische Details eingehen. Betrachten Sie die Implementierung:


  1. Die erforderliche Suchfunktion hat zwei unserer Parameter f, y und gibt das Ergebnis von IIFE zurück.
  2. Eine sofort aufgerufene Funktion implementiert eine binäre Suche nach einer Lösung. Der erste Aufruf erhält den Anfangsbereich.
  3. Die Suche selbst wird durch Rekursion implementiert, die den NFE-Namen für nachfolgende Aufrufe verwendet. Wir deklarieren keine Suchfunktion und erstellen keine neue Variable.

Natürlich kann ein ähnliches Problem durch einen einfachen Zyklus mit einer Vorbedingung gelöst werden, aber dies ist für die Konsolencodierung zu zwingend erforderlich.


Zum Schluss noch ein paar Worte zum Stack Trace. Wir haben eine NFE-Funktion, in deren Hauptteil eine Ausnahme ausgelöst wurde:


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

Da der Ausdruck eine benannte Funktion enthält, wird sie im Stack-Trace angezeigt:


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

Ab ES-2015 erstellen jedoch viele anonyme Funktionsausdrücke tatsächlich eine Funktion mit einem Namen, wodurch sie aus dem Kontext entfernt werden:


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

Die Funktion auf der rechten Seite des Ausdrucks ist der Variablen auf der linken Seite zugeordnet:


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

Dies ist jedoch nicht immer möglich. Ein klassisches Beispiel ist die Initialisierung eines externen Bibliotheksskripts über IIFE als Option:


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

Oder ein häufiges Beispiel für eine Funktion, die eine andere Funktion zurückgibt:


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

Es gibt keine Variable für die Ausgabe, daher wird im Falle einer Ausnahme anonym angezeigt . NFE kann bei Rechtsstreitigkeiten ein wenig helfen.


Kurzer Abschluss


NFE ist gut darin, präzisen und nicht unterstützten Code zu schreiben, um schnell eine Vielzahl von Aufgaben zu prototypisieren. Um ihre Schönheit im endgültigen Code zu verwenden, sollten Sie ein paar Mal darüber nachdenken: JavaScript ist bereits mit interessanten Spezifikationen überladen.

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


All Articles