Einführung in die funktionale Programmierung: JavaScript-Paradigmen
Veröffentlicht: 2022-03-11Funktionale Programmierung ist ein Paradigma zum Erstellen von Computerprogrammen unter Verwendung von Ausdrücken und Funktionen, ohne Zustand und Daten zu verändern.
Durch die Beachtung dieser Einschränkungen zielt die funktionale Programmierung darauf ab, Code zu schreiben, der klarer zu verstehen und fehlerresistenter ist. Dies wird erreicht, indem die Verwendung von Flusssteuerungsanweisungen ( for
, while
, break
, continue
, goto
) vermieden wird, die das Verfolgen des Codes erschweren. Außerdem erfordert die funktionale Programmierung, dass wir reine, deterministische Funktionen schreiben, die weniger wahrscheinlich fehlerhaft sind.
In diesem Artikel werden wir über die funktionale Programmierung mit JavaScript sprechen. Wir werden auch verschiedene JavaScript-Methoden und -Funktionen untersuchen, die dies ermöglichen. Am Ende werden wir verschiedene Konzepte im Zusammenhang mit der funktionalen Programmierung untersuchen und sehen, warum sie so leistungsfähig sind.
Bevor man sich jedoch mit der funktionalen Programmierung beschäftigt, muss man den Unterschied zwischen reinen und unreinen Funktionen verstehen.
Reine vs. unreine Funktionen
Reine Funktionen nehmen einige Eingaben und geben eine feste Ausgabe. Außerdem verursachen sie keine Nebenwirkungen in der Außenwelt.
const add = (a, b) => a + b;
Hier ist add
eine reine Funktion. Dies liegt daran, dass bei einem festen Wert von a
und b
die Ausgabe immer gleich ist.
const SECRET = 42; const getId = (a) => SECRET * a;
getId
ist keine reine Funktion. Der Grund dafür ist, dass es die globale Variable SECRET
zum Berechnen der Ausgabe verwendet. Wenn sich SECRET
ändern würde, gibt die getId
Funktion einen anderen Wert für dieselbe Eingabe zurück. Es handelt sich also nicht um eine reine Funktion.
let id_count = 0; const getId = () => ++id_count;
Dies ist auch eine unreine Funktion, und das aus mehreren Gründen – (1) sie verwendet eine nicht-lokale Variable zur Berechnung ihrer Ausgabe, und (2) sie erzeugt einen Nebeneffekt in der Außenwelt, indem sie eine Variable darin ändert Welt.
Dies kann problematisch sein, wenn wir diesen Code debuggen müssten.
Was ist der aktuelle Wert von id_count
? Welche anderen Funktionen id_count
? Gibt es andere Funktionen, die auf id_count
?
Aus diesen Gründen verwenden wir in der funktionalen Programmierung nur reine Funktionen.
Ein weiterer Vorteil von reinen Funktionen ist, dass sie parallelisiert und gespeichert werden können. Schauen Sie sich die beiden vorherigen Funktionen an. Es ist unmöglich, sie zu parallelisieren oder zu speichern. Dies hilft beim Erstellen von leistungsfähigem Code.
Die Grundsätze der funktionalen Programmierung
Bisher haben wir gelernt, dass die funktionale Programmierung von wenigen Regeln abhängig ist. Sie sind wie folgt.
- Verändern Sie keine Daten
- Verwenden Sie reine Funktionen: feste Ausgabe für feste Eingaben und keine Nebenwirkungen
- Verwenden Sie Ausdrücke und Deklarationen
Wenn wir diese Bedingungen erfüllen, können wir sagen, dass unser Code funktioniert.
Funktionale Programmierung in JavaScript
JavaScript verfügt bereits über einige Funktionen, die eine funktionale Programmierung ermöglichen. Beispiel: String.prototype.slice, Array.protoype.filter, Array.prototype.join.
Andererseits sind Array.prototype.forEach, Array.prototype.push unreine Funktionen.
Man kann argumentieren, dass Array.prototype.forEach
von Natur aus keine unreine Funktion ist, aber denken Sie darüber nach – es ist nicht möglich, irgendetwas damit zu tun, außer nicht lokale Daten zu mutieren oder Nebeneffekte zu erzeugen. Daher ist es in Ordnung, sie in die Kategorie der unreinen Funktionen einzuordnen.
Außerdem hat JavaScript eine const-Deklaration, die sich perfekt für die funktionale Programmierung eignet, da wir keine Daten mutieren werden.
Reine Funktionen in JavaScript
Sehen wir uns einige der reinen Funktionen (Methoden) von JavaScript an.
Filter
Wie der Name schon sagt, filtert dies das Array.
array.filter(condition);
Die Bedingung hier ist eine Funktion, die jedes Element des Arrays abruft, und sie sollte entscheiden, ob das Element beibehalten werden soll oder nicht, und den wahren booleschen Wert dafür zurückgeben.
const filterEven = x => x%2 === 0; [1, 2, 3].filter(filterEven); // [2]
Beachten Sie, dass filterEven
eine reine Funktion ist. Wenn es unrein gewesen wäre, hätte es den gesamten Filteraufruf unrein gemacht.
Karte
map
ordnet jedes Array-Element einer Funktion zu und erstellt basierend auf den Rückgabewerten der Funktionsaufrufe ein neues Array.
array.map(mapper)
mapper
ist eine Funktion, die ein Element eines Arrays als Eingabe nimmt und die Ausgabe zurückgibt.
const double = x => 2 * x; [1, 2, 3].map(double); // [2, 4, 6]
Reduzieren
reduce
das Array auf einen einzelnen Wert.
array.reduce(reducer);
reducer
ist eine Funktion, die den akkumulierten Wert und das nächste Element im Array nimmt und den neuen Wert zurückgibt. Es wird so für alle Werte im Array nacheinander aufgerufen.
const sum = (accumulatedSum, arrayItem) => accumulatedSum + arrayItem [1, 2, 3].reduce(sum); // 6
Konkat
concat
fügt einem bestehenden Array neue Elemente hinzu, um ein neues Array zu erstellen. Es unterscheidet sich von push()
in dem Sinne, dass push()
Daten mutiert, was sie unrein macht.

[1, 2].concat([3, 4]) // [1, 2, 3, 4]
Das Gleiche können Sie auch mit dem Spread-Operator tun.
[1, 2, ...[3, 4]]
Objektzuweisung
Object.assign
kopiert Werte aus dem bereitgestellten Objekt in ein neues Objekt. Da die funktionale Programmierung auf unveränderlichen Daten basiert, verwenden wir sie, um neue Objekte auf der Grundlage bestehender Objekte zu erstellen.
const obj = {a : 2}; const newObj = Object.assign({}, obj); newObj.a = 3; obj.a; // 2
Mit dem Aufkommen von ES6 kann dies auch mit dem Spread-Operator erfolgen.
const newObj = {...obj};
Erstellen Sie Ihre eigene reine Funktion
Wir können auch unsere reine Funktion erschaffen. Machen wir eine, um eine Zeichenfolge n
-mal zu duplizieren.
const duplicate = (str, n) => n < 1 ? '' : str + duplicate(str, n-1);
Diese Funktion dupliziert einen String n
-mal und gibt einen neuen String zurück.
duplicate('hooray!', 3) // hooray!hooray!hooray!
Funktionen höherer Ordnung
Funktionen höherer Ordnung sind Funktionen, die eine Funktion als Argument akzeptieren und eine Funktion zurückgeben. Oft werden sie verwendet, um die Funktionalität einer Funktion zu erweitern.
const withLog = (fn) => { return (...args) => { console.log(`calling ${fn.name}`); return fn(...args); }; };
Im obigen Beispiel erstellen wir eine withLog
-Funktion höherer Ordnung, die eine Funktion übernimmt und eine Funktion zurückgibt, die eine Nachricht protokolliert, bevor die umschlossene Funktion ausgeführt wird.
const add = (a, b) => a + b; const addWithLogging = withLog(add); addWithLogging(3, 4); // calling add // 7
withLog
HOF kann auch mit anderen Funktionen verwendet werden und es funktioniert ohne Konflikte oder das Schreiben von zusätzlichem Code. Das ist das Schöne an einem HOF.
const addWithLogging = withLog(add); const hype = s => s + '!!!'; const hypeWithLogging = withLog(hype); hypeWithLogging('Sale'); // calling hype // Sale!!!
Man kann es auch aufrufen, ohne eine Kombinationsfunktion zu definieren.
withLog(hype)('Sale'); // calling hype // Sale!!!
Curry
Currying bedeutet, eine Funktion, die mehrere Argumente aufnimmt, in eine oder mehrere Ebenen von Funktionen höherer Ordnung zu zerlegen.
Nehmen wir die add
-Funktion.
const add = (a, b) => a + b;
Wenn wir es Curry machen sollen, schreiben wir es um, indem wir die Argumente wie folgt auf mehrere Ebenen verteilen.
const add = a => { return b => { return a + b; }; }; add(3)(4); // 7
Der Vorteil des Currying ist das Auswendiglernen. Wir können jetzt bestimmte Argumente in einem Funktionsaufruf speichern, sodass sie später ohne Duplizierung und Neuberechnung wiederverwendet werden können.
// assume getOffsetNumer() call is expensive const addOffset = add(getOffsetNumber()); addOffset(4); // 4 + getOffsetNumber() addOffset(6);
Das ist sicherlich besser, als überall beide Argumente zu verwenden.
// (X) DON"T DO THIS add(4, getOffsetNumber()); add(6, getOffsetNumber()); add(10, getOffsetNumber());
Wir können unsere Curry-Funktion auch so umformatieren, dass sie prägnant aussieht. Dies liegt daran, dass jede Ebene des currying-Funktionsaufrufs eine einzeilige Rückgabeanweisung ist. Daher können wir Pfeilfunktionen in ES6 verwenden, um es wie folgt umzugestalten.
const add = a => b => a + b;
Komposition
In der Mathematik ist Komposition definiert als das Übergeben der Ausgabe einer Funktion an die Eingabe einer anderen, um eine kombinierte Ausgabe zu erzeugen. Dasselbe ist in der funktionalen Programmierung möglich, da wir reine Funktionen verwenden.
Um ein Beispiel zu zeigen, erstellen wir einige Funktionen.
Die erste Funktion ist range, die eine Startnummer a
und eine Endnummer b
nimmt und ein Array erstellt, das aus Zahlen von a
bis b
besteht.
const range = (a, b) => a > b ? [] : [a, ...range(a+1, b)];
Dann haben wir eine Funktion multiplizieren, die ein Array nimmt und alle darin enthaltenen Zahlen multipliziert.
const multiply = arr => arr.reduce((p, a) => p * a);
Wir werden diese Funktionen zusammen verwenden, um die Fakultät zu berechnen.
const factorial = n => multiply(range(1, n)); factorial(5); // 120 factorial(6); // 720
Die obige Funktion zur Berechnung der Fakultät ähnelt f(x) = g(h(x))
, wodurch die Zusammensetzungseigenschaft demonstriert wird.
Schlussworte
Wir haben reine und unreine Funktionen, funktionale Programmierung, die neuen JavaScript-Funktionen, die dabei helfen, und einige Schlüsselkonzepte der funktionalen Programmierung durchgegangen.
Wir hoffen, dass dieser Artikel Ihr Interesse an funktionaler Programmierung weckt und Sie möglicherweise dazu motiviert, es in Ihrem Code auszuprobieren. Wir sind überzeugt, dass es eine Lernerfahrung und ein Meilenstein auf Ihrem Weg zur Softwareentwicklung sein wird.
Funktionale Programmierung ist ein gut erforschtes und robustes Paradigma zum Schreiben von Computerprogrammen. Mit der Einführung von ES6 ermöglicht JavaScript ein viel besseres funktionales Programmiererlebnis als je zuvor.