Grundlagen der funktionalen Programmierung in JavaScript

Grundlagen der funktionalen Programmierung in JavaScript

1. Einleitung

Funktionale Programmierung hat eine lange Geschichte, ist aber in den letzten Jahren häufig ins Blickfeld der Öffentlichkeit gerückt. Viele Sprachen, die keine funktionale Programmierung unterstützen, fügen aktiv sehr typische Funktionen der funktionalen Programmierung hinzu, wie beispielsweise Closures und anonyme Funktionen. Viele Front-End-Frameworks rühmen sich auch damit, die Funktionen der funktionalen Programmierung zu nutzen, als wären sie sehr fortgeschritten, wenn man sie mit der funktionalen Programmierung in Verbindung bringt. Darüber hinaus gibt es einige Frameworks und Bibliotheken speziell für die funktionale Programmierung, wie z. B.: RxJS, cycleJS, ramdaJS, lodashJS, underscoreJS usw. Funktionale Programmierung erfreut sich immer größerer Beliebtheit. Die Beherrschung dieses Programmierparadigmas ist für das Schreiben von qualitativ hochwertigem und wartbarem Code von Vorteil, daher ist es für uns notwendig, es zu beherrschen.

2. Was ist funktionale Programmierung?

Wikipedia-Definition: Funktionale Programmierung (englisch: functional programming), auch funktionale Programmierung genannt, ist ein Programmierparadigma, das Computeroperationen als mathematische Funktionsberechnungen betrachtet und die Verwendung von Programmzuständen und veränderlichen Objekten vermeidet.

3. Reine Funktionen (der Grundstein der funktionalen Programmierung, Funktionen ohne Nebeneffekte)

In der Mathematik der Mittelstufe lautet die Definition der Funktion f: Für die Eingabe x erzeugt sie eine eindeutige Ausgabe y=f(x). Dies ist eine reine Funktion. Es erfüllt zwei Bedingungen: 1. Diese Funktion erzeugt immer die gleiche Ausgabe, wenn der Eingabewert derselbe ist. Die Ausgabe einer Funktion ist unabhängig vom Kontext der aktuellen Ausführungsumgebung. 2. Der Ausführungsprozess dieser Funktion wirkt sich nicht auf die Ausführungsumgebung aus, d. h. es treten keine Nebeneffekte auf (z. B. Auslösen von Ereignissen, Initiieren von HTTP-Anforderungen, Drucken/Protokollieren usw.). Einfach ausgedrückt: Wenn die Ausgabe einer Funktion nicht von der äußeren Umgebung beeinflusst wird und auch keine Auswirkungen auf die äußere Umgebung hat, handelt es sich bei der Funktion um eine reine Funktion, das heißt, sie konzentriert sich nur auf logische Operationen und mathematische Operationen, und dieselbe Eingabe führt immer zur gleichen Ausgabe. Zu den integrierten Funktionen von JavaScript gehören viele reine und viele unreine Funktionen.

Reine Funktionen: Array.prototype.sliceArray.prototype.mapString.prototype.toUpperCase

Unreine Funktionen: Math.randomDate.nowArray.propertytype.splice

Hier nehmen wir die Slice- und Splice-Methoden als Beispiele:

var xs = [1,2,3,4,5];
// Reines xs.slice(0,3);
//=> [1,2,3]
xs.Scheibe(0,3);
//=> [1,2,3]
xs.Scheibe(0,3);
//=> [1,2,3]

// Unrein xs.splice(0,3);
//=> [1,2,3]
xs.splice(0,3);
//=> [4,5]
xs.splice(0,3);
//=> []


Wir können sehen, dass der Aufruf der Slice-Methode eines Arrays jedes Mal genau dasselbe Ergebnis zurückgibt und xs sich nicht ändert, während der Aufruf der Splice-Methode jedes Mal einen anderen Wert zurückgibt und xs nicht mehr erkennbar ist. Aus diesem Grund legen wir Wert auf die Verwendung reiner Funktionen, da reine Funktionen im Hinblick auf Cachefähigkeit, Portabilität, Testbarkeit und parallele Datenverarbeitung enorme Vorteile gegenüber unreinen Funktionen bieten. Hier nehmen wir die Cachefähigkeit als Beispiel:

var Quadratzahl = memoize(Funktion(x){ return x*x; });
Quadratzahl(4);
//=> 16
squareNumber(4); // Lies das Ergebnis des Eingabewertes 4 aus dem Cache //=> 16


Wie machen wir also eine unreine Funktion rein? Beispielsweise die folgende Funktion:

var Minimum = 21;
var checkAge = Funktion(Alter) {
  Rückgabealter >= Minimum;
};


Der Rückgabewert dieser Funktion hängt vom Wert der veränderbaren Variable Minimum ab, die wiederum vom Systemzustand abhängt. In großen Systemen ist diese Abhängigkeit von externen Zuständen eine Hauptursache für die Systemkomplexität.

var checkAge = Funktion(Alter) {
  var Minimum = 21;
  Rückgabealter >= Minimum;
};


Durch die Transformation haben wir checkAge in eine reine Funktion verwandelt, die nicht vom Systemstatus abhängt. Das Minimum ist jedoch fest codiert definiert, was die Skalierbarkeit der Funktion einschränkt. Wie man dieses Problem mit funktionalen Methoden elegant lösen kann, sehen wir im folgenden Currying. Der grundlegende Weg, eine Funktion rein zu machen, besteht deshalb darin, sich nicht auf den Systemzustand zu verlassen.

4. Funktions-Currying

Das Konzept des Currying ist einfach: Der Vorgang der Umwandlung einer Funktion niedriger Ordnung in eine Funktion höherer Ordnung wird als Currying bezeichnet.

Um eine anschauliche Metapher zu verwenden:

Beispielsweise können wir für die Additionsoperation: var add = (x, y) => x + y das Curry wie folgt ausführen:

//es5 schreibt var add = function(x) {
  Rückgabefunktion (y) {
    gib x + y zurück;
  };
};

//es6 schreibt var add = x => (y => x + y);

//Probieren Sie es aus var increment = add(1);
var addTen = add(10);

inkrement(2); // 3

addTen(2); // 12

Bei einer sehr einfachen Funktion wie der Addition hilft Currying nicht. Erinnern Sie sich an die oben stehende checkAge-Funktion? Wir können es so curryen:

var checkage = min => (Alter => Alter > min);
var checkage18 = checkage(18);
checkage18(20);
// =>wahr


Dies zeigt, dass Funktions-Currying die Möglichkeit ist, eine Funktion „vorzuladen“, ihr ein oder zwei Argumente zu übergeben und eine neue Funktion zu erhalten, die sich an diese Argumente erinnert. In gewissem Sinne handelt es sich dabei um eine Art Zwischenspeichern von Parametern, was eine sehr effiziente Möglichkeit zum Schreiben von Funktionen darstellt:

var curry = erforderlich('lodash').curry;

//Currying zweier reiner Funktionen var match = curry((what, str) => str.match(what));
var filter = curry((f, ary) => ary.filter(f));

// Prüfen, ob die Zeichenfolge Leerzeichen enthält var hasSpaces = match(/\s+/g);

hasSpaces("hallo Welt"); // [ ' ' ]
hasSpaces("leerzeichenlos"); // null

var findSpaces = filter(hasSpaces);

findSpaces(["tori_spelling", "tori amos"]); // ["tori amos"]

5. Funktionszusammensetzung

Angenommen, wir müssen einige Spaltenoperationen an einem String durchführen, wie unten gezeigt. Der Einfachheit halber führen wir nur zwei Operationen an einem String durch. Wir definieren eine neue Funktion shout, rufen zuerst toUpperCase auf und übergeben dann den Rückgabewert an die exclaim-Funktion. Was ist daran falsch? Das ist nicht elegant. Wenn viele Dinge zu tun sind, sind die verschachtelten Funktionen sehr tief und der Code wird von innen nach außen ausgeführt, was nicht intuitiv ist. Wir hoffen, dass der Code von rechts nach links ausgeführt wird. Zu diesem Zeitpunkt müssen wir eine Kombination verwenden.

var toUpperCase = Funktion(x) { return x.toUpperCase(); };
var ausrufen = Funktion(x) { return x + '!'; };

var shout = Funktion(x){
  returniere exclaim(toUpperCase(x));
};

schreien("schickt die Clowns rein");
//=> „SCHICKT DIE CLOWNS HEREIN!“

Mithilfe der Komposition können wir unsere Shout-Funktion wie folgt definieren:

//Definiere „Komposition“
var compose = (...args) => x => args.reduceRight((Wert, Element) => Element(Wert), x);

var toUpperCase = Funktion(x) { return x.toUpperCase(); };
var ausrufen = Funktion(x) { return x + '!'; };

var shout = verfassen(exclaim, toUpperCase);

schreien("schickt die Clowns rein");
//=> „SCHICKT DIE CLOWNS HEREIN!“

Der Code wird von rechts nach links ausgeführt, was sehr klar und leicht verständlich ist. Die von uns definierte Zusammensetzung ist wie ein N-seitiger Klebstoff, der eine beliebige Anzahl reiner Funktionen miteinander kombinieren kann. Diese flexible Kombination ermöglicht es uns, funktionalen Code wie Bausteine ​​zu kombinieren:

var head = Funktion(x) { return x[0]; };
var reverse = reduzieren(Funktion(acc, x){ return [x].concat(acc); }, []);
var last = komponieren (Kopf, umgekehrt);

zuletzt(['Jumpkick', 'Roundhouse', 'Uppercut']);
//=> 'Aufwärtshaken'

6. Deklarativer und imperativer Code

Imperativer Code: sagt der Maschine, wie sie Dinge tun soll (wie), sodass sie, egal was Sie wollen (was), es gemäß Ihrem Befehl tut. Deklarativer Code: Sagen Sie der „Maschine“, was Sie wollen (was), und lassen Sie die Maschine herausfinden, wie es geht (wie). Im Gegensatz zum Imperativ bedeutet Deklarativ, dass wir Ausdrücke und keine schrittweisen Anweisungen schreiben. Nehmen wir SQL als Beispiel. Es gibt keinen Befehl „mach zuerst dies, dann mach das“. Stattdessen gibt es nur einen Ausdruck, der angibt, welche Daten wir aus der Datenbank abrufen möchten. Über die Art des Datenabrufs wird individuell entschieden. Unabhängig davon, ob es sich in Zukunft um ein Datenbank-Upgrade oder eine SQL-Engine-Optimierung handelt, besteht keine Notwendigkeit, die Abfrageanweisung überhaupt zu ändern. Dies liegt daran, dass es mehrere Möglichkeiten gibt, einen Ausdruck zu analysieren und dasselbe Ergebnis zu erhalten. Schauen wir uns zum besseren Verständnis ein Beispiel an:

// Imperativ var macht = [];
für (var i = 0; i < Autos.Länge; i++) {
  macht.push(Autos[i].machen);
}

// Deklarative Variable makes = cars.map(function(car){ return car.make; });

Die imperative Schleife erfordert, dass Sie zuerst ein Array instanziieren müssen. Der Interpreter führt den nachfolgenden Code erst aus, nachdem die Instanziierungsanweisung ausgeführt wurde. Anschließend durchlaufen Sie einfach die Liste der Autos und erhöhen den Zähler manuell, als würden Sie ein Auto fahren, bei dem alle Teile sichtbar sind. Das ist nicht die Aufgabe eines guten Programmierers. Die deklarative Schreibweise ist ein Ausdruck und die Details zur Iteration des Zählers und zum Sammeln des zurückgegebenen Arrays sind verborgen. Es gibt vor, was zu tun ist, nicht, wie es zu tun ist. Die Map-Funktion ist nicht nur klarer und prägnanter, sondern kann auch unabhängig voneinander weiter optimiert werden und sogar die im Interpreter integrierte, extrem schnelle Map-Funktion verwenden, sodass unser Hauptgeschäftscode nicht geändert werden muss. Ein offensichtlicher Vorteil der funktionalen Programmierung ist dieser deklarative Code. Bei reinen Funktionen ohne Nebeneffekte müssen wir uns keine Gedanken darüber machen, wie die Funktion intern implementiert wird, und können uns auf das Schreiben des Geschäftscodes konzentrieren. Bei der Codeoptimierung müssen Sie sich nur auf diese stabilen und soliden Funktionen konzentrieren. Im Gegenteil: Unreiner, nicht-funktionaler Code erzeugt Nebenwirkungen oder ist von der externen Systemumgebung abhängig. Diese unsauberen Nebenwirkungen sollten Sie bei der Verwendung immer berücksichtigen. Bei komplexen Systemen kann dies für den Programmierer eine enorme geistige Belastung darstellen.

7. Punktfrei

Der Punktfreie Modus bedeutet, dass Sie Ihre Daten nie preisgeben müssen. Das bedeutet, dass die Funktion nicht angeben muss, mit welcher Art von Daten sie arbeitet. Erstklassige Funktionen, Currying und Komposition sind eine große Hilfe beim Erreichen dieses Musters.

// Nicht punktfrei, da Daten erwähnt werden: Wort
var snakeCase = Funktion (Wort) {
  returniere word.toLowerCase().replace(/\s+/ig, '_');
};

// punktefrei
var snakeCase = zusammensetzen(ersetzen(/\s+/ig, '_'), toLowerCase);

Dieser Stil kann uns helfen, unnötige Benennungen zu reduzieren und den Code prägnant und universell zu halten. Um einige Funktionen im Point-Free-Stil zu schreiben, müssen andere Stellen im Code natürlich weniger Point-Free sein, und Sie müssen hier Ihre eigenen Entscheidungen treffen.

8. Beispielanwendung

Mit dem oben genannten Wissen ist es an der Zeit, eine Beispielbewerbung zu schreiben. Hier verwenden wir Ramda anstelle von Lodash oder anderen Bibliotheken. Ramda bietet viele Funktionen wie Komponieren und Curry. Unsere Anwendung wird vier Dinge tun:

1. Erstellen Sie eine URL basierend auf bestimmten Suchbegriffen

2. Senden Sie eine API-Anfrage an Flickr

3. Konvertieren Sie das zurückgegebene JSON in ein HTML-Bild

4. Platzieren Sie das Bild auf dem Bildschirm. Oben wurden zwei unreine Aktionen erwähnt, nämlich das Abrufen von Daten von der API von Flickr und das Platzieren des Bildes auf dem Bildschirm. Definieren wir zunächst diese beiden Aktionen, damit wir sie isolieren können. Hier kapseln wir einfach die getJSON-Funktion von jQuery, machen daraus eine Curry-Funktion und tauschen auch die Parameter aus. Wir platzieren sie im Impure-Namespace, um sie zu isolieren, damit wir wissen, dass es sich um gefährliche Funktionen handelt. Mithilfe der Techniken Function Currying und Function Composition können wir eine funktionale Anwendung erstellen:

Adresse der Vorschau: https://code.h5jun.com/vixe/1/edit?html,js,output Sehen Sie, was für eine wunderbare deklarative Spezifikation, sie sagt nur, was zu tun ist, aber nicht, wie es zu tun ist. Jetzt können wir uns jede Codezeile als eine Gleichung vorstellen, und die durch die Variablennamen dargestellten Eigenschaften sind die Bedeutung der Gleichung.

IX. Fazit

Wir haben gesehen, wie wir unsere neuen Fähigkeiten in einer kleinen, aber realistischen Anwendung anwenden können, aber wie steht es mit der Ausnahmebehandlung und der Code-Verzweigung? Wie können wir die gesamte Anwendung funktionsfähig machen, anstatt nur destruktive Funktionen in einen Namespace zu setzen? Wie können Anwendungen sicherer und ausdrucksstärker gemacht werden? Im nächsten Artikel werde ich fortgeschrittenere Kenntnisse der funktionalen Programmierung vorstellen, etwa Functor, Monad, Applicative und andere Konzepte.

Oben finden Sie ausführliche Informationen zu den Grundlagen der funktionalen Programmierung in JavaScript. Weitere Informationen zur funktionalen Programmierung in JavaScript finden Sie in den anderen verwandten Artikeln auf 123WORDPRESS.COM!

Das könnte Sie auch interessieren:
  • JavaScript-Grundlagen: Funktion zur sofortigen Ausführung
  • Grundlagen der JavaScript-Funktionen
  • Zusammenfassung der JavaScript-Basisfunktionen
  • Grundlegende Einführung in Javascript-Zeitfunktionen
  • JavaScript-Grundlagen Funktion Detaillierte Erklärung

<<:  Zusammenfassung der MySQL-Indexkenntnisse

>>:  So zeigen Sie alle laufenden Prozesse in Linux an

Artikel empfehlen

Wofür wird jQuery verwendet? jQuery ist eigentlich ein js-Framework

Einführung in jQuery Die jQuery-Bibliothek kann e...

So führen Sie das Springboot-Projekt im Docker aus

1. Klicken Sie unten in IDEA auf Terminal und geb...

Vollbild-Drag-Upload-Komponente basierend auf Vue3

In diesem Artikel wird hauptsächlich die Vollbild...

Das Vue-Projekt implementiert einen grafischen Überprüfungscode

In diesem Artikelbeispiel wird der spezifische Co...

Detaillierte Schritte zur Installation von Mysql5.7.19 mit yum auf Centos7

In der Yum-Quelle von Centos7 ist standardmäßig k...

Detaillierter Vue-Code zur Implementierung der Shuttle-Box-Funktion

Vue - Implementierung der Shuttle-Box-Funktion. D...

So verwenden Sie Web-Frontend-Vektorsymbole

Vorwort Beim Schreiben von Frontend-Seiten verwen...

So deinstallieren und installieren Sie Tomcat neu (mit Bildern und Text)

Deinstallieren Sie tomcat9 1. Da die Installation...

JavaScript implementiert Produktdetails der E-Commerce-Plattform

In diesem Artikel wird ein allgemeines Beispiel f...

Webdesign-Zusammenfassung

<br />Von der Geburt meiner ersten persönlic...