JavaScript Currying und partielle Anwendung
Currying und partielle Anwendung sind leistungsstarke Techniken der funktionalen Programmierung, die Modularität und Wiederverwendbarkeit in JavaScript verbessern.
Currying und partielle Anwendung sind leistungsstarke Techniken der funktionalen Programmierung, die die Modularität und Wiederverwendbarkeit Ihres JavaScript-Codes erheblich verbessern können. Beide basieren auf Closures – der Fähigkeit einer inneren Funktion, sich die Variablen der äußeren Funktion zu merken, die sie erzeugt hat –, sodass eine zurückgegebene Funktion die bereits übergebenen Argumente beibehält.
Dieser Artikel erklärt, was Currying ist und warum es wichtig ist, wie man einen wiederverwendbaren curry-Helfer schreibt, wie sich die partielle Anwendung vom Currying unterscheidet, reale Anwendungsfälle sowie Best Practices (und Fallstricke), die man im Blick behalten sollte.
Currying verstehen
Currying ist eine Technik, bei der eine Funktion in eine Folge von Funktionen umgewandelt wird, von denen jede ein einzelnes Argument entgegennimmt. Damit lässt sich eine Funktion, die mehrere Argumente benötigt, in eine Reihe unärer (einargumentiger) Funktionen aufteilen.
Beispiel für Currying
Betrachten Sie eine einfache Funktion, die drei Zahlen addiert:
function add(a, b, c) {
return a + b + c;
}Wir können diese Funktion in eine gecurryte Version umwandeln:
Erklärung:
- Die
add-Funktion nimmt drei Argumente entgegen und gibt deren Summe zurück. - Die
curryAdd-Funktion ist eine gecurryte Version vonadd. Sie nimmt ein Argumentaund gibt eine weitere Funktion zurück, diebentgegennimmt. Diese zweite Funktion gibt wiederum eine Funktion zurück, diecannimmt. Die innerste Funktion gibt die Summe vona,bundczurück.
Vorteile von Currying
- Wiederverwendbarkeit: Gecurryte Funktionen ermöglichen es, neue Funktionen durch Fixieren einiger Argumente zu erstellen. Dies verbessert die Wiederverwendbarkeit, indem Sie leicht spezialisierte Versionen einer Funktion erstellen können, ohne Code zu duplizieren.
Hier ist curriedAdd eine gecurryte Version der add-Funktion. Sie ermöglicht es uns, spezialisierte Funktionen wie add5 zu erstellen, indem das erste Argument (a) auf 5 fixiert wird. Dies fördert die Wiederverwendung von Code, da wir mehrere spezialisierte Funktionen erstellen können, ohne die Additions-Logik zu wiederholen.
- Funktionskomposition: Currying erleichtert die Komposition von Funktionen. Funktionskomposition ist der Prozess, zwei oder mehr Funktionen zu einer neuen Funktion zu kombinieren.
In diesem Beispiel komponieren wir die Funktionen multiply und addOne mithilfe von Currying. Die prägnante Form a => b => a * b basiert auf Pfeilfunktionen, die verschachtelte einargumentige Funktionen leicht lesbar machen. Durch das Currying dieser Funktionen können wir leicht eine neue Funktion addOneThenMultiplyBy5 erstellen, die zuerst 1 zur Eingabe (x) addiert und das Ergebnis dann mit 5 multipliziert. Dies zeigt, wie Currying die Funktionskomposition erleichtert und es vereinfacht, neue Funktionen durch Kombinieren bestehender zu erstellen.
Currying in JavaScript implementieren
Wir können eine Hilfsfunktion erstellen, um beliebige Funktionen zu curryen. Hier ist eine Implementierung einer generischen Currying-Funktion:
Erklärung:
-
Currying-Funktion (
curry):- Die
curry-Funktion nimmt eine andere Funktionfnals Eingabe entgegen. - Sie gibt eine neue Funktion namens
curriedzurück. - Diese Funktion
curriednimmt beliebig viele Argumente mithilfe der Rest-Parameter-Syntax (...args) entgegen.
- Die
-
Gecurryte Funktion (
curried):- Innerhalb von
curriedwird geprüft, ob die Anzahl der übergebenen Argumente (args.length) größer oder gleich der Anzahl der von der ursprünglichen Funktionfnerwarteten Argumente (fn.length) ist. - Wenn genügend Argumente übergeben wurden, wird die ursprüngliche Funktion
fnmit diesen Argumenten überfn.apply(this, args)aufgerufen. - Wenn nicht genügend Argumente übergeben wurden, wird eine neue Funktion zurückgegeben, die weitere Argumente (
nextArgs) mithilfe des Spread-Operators (...nextArgs) akzeptiert. - Diese neue Funktion ruft
curriedrekursiv mit den kombinierten Argumenten (args.concat(nextArgs)) auf und stellt sicher, dass alle Argumente gesammelt werden, bevor die ursprüngliche Funktionfnaufgerufen wird.
Hinweis:
fn.lengthzählt nur Parameter ohne Standardwerte und ignoriert Rest-Parameter. - Innerhalb von
-
Beispielverwendung:
- Wir definieren eine
multiply-Funktion, die drei Argumente annimmt und deren Produkt zurückgibt. - Wir erstellen eine gecurryte Version der
multiply-Funktion, indem wir sie an diecurry-Funktion übergeben, die eine neue FunktioncurriedMultiplyzurückgibt. curriedMultiplykann nun mit einem, zwei oder drei Argumenten aufgerufen werden.- Bei jedem Aufruf von
curriedMultiplymit einem oder mehreren Argumenten gibt sie eine neue Funktion zurück, bis alle Argumente gesammelt sind – dann gibt sie das Produkt der Argumente zurück.
- Wir definieren eine
Partielle Anwendung erkunden
Partielle Anwendung ist eine Technik, bei der Sie eine neue Funktion erstellen, indem Sie einige Argumente der ursprünglichen Funktion vorab belegen. Dies ist besonders nützlich, um spezialisierte Funktionen zu erstellen.
Beispiel für partielle Anwendung
Betrachten Sie die folgende Funktion, die eine Nachricht formatiert:
function formatMessage(greeting, name) {
return `${greeting}, ${name}!`;
}Wir können eine partiell angewandte Funktion erstellen:
Erklärung:
- Die
formatMessage-Funktion nimmt zwei Argumente entgegen,greetingundname, und gibt eine formatierte Nachricht zurück. - Die
partial-Funktion nimmt eine Funktionfnund einige voreingestellte Argumente (...presetArgs) entgegen. Sie gibt eine neue Funktion zurück, die die verbleibenden Argumente (...laterArgs) akzeptiert. - Wenn die neue Funktion aufgerufen wird, kombiniert sie
presetArgsundlaterArgsund ruft die ursprüngliche Funktionfnmit diesen Argumenten auf. - Mit
partialerstellen wirgreetHello, eine Funktion, die immer „Hello" als Begrüßung verwendet. Wenn sie mit einem Namen aufgerufen wird, gibt sie die vollständige Nachricht zurück.
Vorteile der partiellen Anwendung
-
Vereinfachung: Einfachere Funktionen aus komplexeren erstellen
Angenommen, wir haben eine Funktion, die den Endpreis eines Artikels nach Abzug von Rabatt und Steuer berechnet.
function calculateFinalPrice(price, discount, tax) {
return price - (price * discount) + (price * tax);
}Diese Funktion erfordert drei Argumente, was ihre wiederholte Verwendung etwas umständlich macht, wenn Rabatt und Steuersatz oft gleich sind. Mit partieller Anwendung lässt sich das vereinfachen.
Im obigen Beispiel ist applyDiscountAndTax eine partiell angewandte Funktion, die die Werte discount und tax vorbelegt. Dadurch ist es einfacher, den Endpreis für verschiedene Artikel zu berechnen, ohne Rabatt und Steuersatz wiederholt anzugeben.
-
Code-Wiederverwendbarkeit: Gemeinsame Funktionslogik mit verschiedenen voreingestellten Argumenten wiederverwenden
Stellen Sie sich vor, wir haben eine Funktion, die Nachrichten mit verschiedenen Schweregraden protokolliert.
function logMessage(level, message) {
console.log(`[${level}] ${message}`);
}Wir können mit partieller Anwendung wiederverwendbare Funktionen für verschiedene Log-Level erstellen.
Hier ist createLogger eine partiell angewandte Funktion, die das Argument level festlegt. Die Funktionen infoLogger und errorLogger können nun verwendet werden, um Nachrichten mit den voreingestellten Log-Leveln zu protokollieren, und nutzen dabei die gemeinsame Logik von logMessage.
- Verbesserte Lesbarkeit: Code lesbarer machen, indem komplexe Funktionen aufgeteilt werden Betrachten Sie eine Funktion, die Datumsangaben in verschiedenen Formaten formatiert.
function formatDate(date, format) {
const options = { year: 'numeric', month: '2-digit', day: '2-digit' };
if (format === 'US') {
options.month = 'long';
} else if (format === 'EU') {
options.day = 'numeric';
options.month = 'numeric';
}
return new Date(date).toLocaleDateString(undefined, options);
}Durch partielle Anwendung können wir besser lesbare Funktionen für verschiedene Datumsformate erstellen.
createDateFormatter wendet das Argument format partiell an und erzeugt so spezifische Funktionen für US- und EU-Datumsformate. Diese Aufteilung macht den Code lesbarer und leichter verständlich, da jede Formatter-Funktion einem bestimmten Format gewidmet ist.
Diese Beispiele veranschaulichen, wie partielle Anwendung in JavaScript komplexe Funktionen vereinfachen, die Code-Wiederverwendbarkeit verbessern und die Lesbarkeit erhöhen kann, wodurch der Code leichter zu verwalten und zu verstehen ist.
Currying vs. partielle Anwendung
Diese beiden Techniken sind eng verwandt und werden oft verwechselt, sind jedoch nicht identisch:
- Currying wandelt eine Funktion mit N Argumenten in N verschachtelte Funktionen um, die jeweils genau ein Argument entgegennehmen. Eine vollständig gecurryte Funktion wird immer ein Argument nach dem anderen aufgerufen:
f(a)(b)(c). - Partielle Anwendung fixiert einige Argumente einer Funktion und gibt eine neue Funktion zurück, die die restlichen Argumente in einem einzigen Aufruf entgegennimmt:
g = partial(f, a), danng(b, c).
Kurz gesagt: Currying betrifft die Form der Funktion (eine Kette unärer Aufrufe), während es bei der partiellen Anwendung um das Vorbelegen von Argumenten geht. Beachten Sie, dass der generische curry-Helfer oben flexibler als striktes Currying ist – er akzeptiert Argumente in Gruppen (curriedMultiply(2, 3)(4)) und unterstützt damit effektiv auch partielle Anwendung.
Best Practices für Currying und partielle Anwendung
Funktionen rein halten
- Stellen Sie sicher, dass gecurryte und partiell angewandte Funktionen frei von Seiteneffekten bleiben. Das macht sie einfacher zu verstehen und zu testen.
Gezielt einsetzen
- Verwenden Sie Currying und partielle Anwendung, wenn sie sich natürlich für das vorliegende Problem eignen.
- Vermeiden Sie den übermäßigen Einsatz dieser Techniken, da sie den Code schwerer verständlich machen können, wenn sie nicht behutsam verwendet werden.
Funktionskomposition nutzen
- Kombinieren Sie gecurryte Funktionen, um komplexere Funktionalität zu erstellen. Currying harmoniert gut mit Funktionskomposition und führt zu modularerem Code.
Dokumentation und Benennung
- Dokumentieren Sie gecurryte und partiell angewandte Funktionen ordnungsgemäß, um deren erwartete Verwendung zu erläutern.
- Verwenden Sie klare und aussagekräftige Funktionsnamen, die ihren Zweck vermitteln.
Diese Fallstricke beachten
fn.lengthist für automatisches Currying unzuverlässig. Ein generischercurry-Helfer, der auffn.lengthbasiert, verhält sich bei Standardparametern oder Rest-Parametern falsch, da diese nicht gezählt werden. Wenn eine Funktion sie verwendet, übergeben Sie die erwartete Arität explizit, anstattfn.lengthzu vertrauen.- Übermäßiges Currying schadet der Lesbarkeit. Eine lange Kette
f(a)(b)(c)(d)ist schwerer zu lesen als ein einzelner, gut benannter Aufruf. Greifen Sie auf Currying zurück, wenn es echte Wiederholungen vermeidet. thisim Auge behalten. Pfeilfunktionen haben kein eigenesthis, daher kann eine gecurryte Kette, die mit Pfeilen geschrieben ist, nicht als Methode verwendet werden, die auf das aufrufende Objekt angewiesen ist. Wenn Sie das dynamischethisbenötigen, lesen Sie den Abschnitt über Funktionsbindung.
Wenn Sie Currying und partielle Anwendung häufig einsetzen, sollten Sie die Lodash-Bibliothek in Betracht ziehen, die praktische Hilfsfunktionen wie _.curry und _.partial bereitstellt.
Kombination mit Array-Methoden
Currying lässt sich wirkungsvoll mit Array-Methoden wie map, filter und reduce kombinieren, um prägnanten und ausdrucksstarken Code zu schreiben.
Erklärung:
- Die
curriedMultiply-Funktion wird verwendet, ummultiplyByTwozu erstellen, eine Funktion, die ihr Argument mit 2 multipliziert. - Das
numbers-Array wird mitmaptransformiert, wobeimultiplyByTwoauf jedes Element angewendet wird und ein neues Array mit verdoppelten Zahlen entsteht.
Fazit
Currying und partielle Anwendung in JavaScript bieten leistungsstarke Techniken zur Vereinfachung der Funktionskomposition, zur Verbesserung der Code-Wiederverwendbarkeit und zur Steigerung der Lesbarkeit. Currying wandelt Funktionen mit mehreren Argumenten in eine Reihe unärer Funktionen um und ermöglicht so flexibleren und modulareren Code. Partielle Anwendung ermöglicht das Vorbelegen von Funktionsargumenten, was die Wiederverwendung von Code und die Vereinfachung komplexer Funktionen erleichtert. Durch den Einsatz dieser Konzepte der funktionalen Programmierung können Entwickler saubereren, prägnanteren und besser wartbaren JavaScript-Code schreiben.