So starten Sie schnell mit VUE3.0: Geben Sie lernbezogene
Empfehlungen ein: JavaScript-Lern-Tutorial
Ich habe viele Erklärungen zur funktionalen Programmierung gesehen, aber die meisten davon sind auf theoretischer Ebene und einige beziehen sich nur auf reine funktionale Programmiersprachen wie Haskell. Der Zweck dieses Artikels besteht darin, über die spezifische Praxis der funktionalen Programmierung in JavaScript in meinen Augen zu sprechen. Der Grund, warum er „in meinen Augen“ lautet, bedeutet, dass das, was ich sage, nur meine persönliche Meinung wiedergibt, was möglicherweise im Widerspruch zu einigen strengen Konzepten steht.
In diesem Artikel werden viele formale Konzepteinführungen weggelassen und der Schwerpunkt wird darauf gelegt, zu zeigen, was Funktionscode in JavaScript ist, was der Unterschied zwischen Funktionscode und allgemeinem Schreiben ist, welche Vorteile Funktionscode uns bringen kann und welche allgemeinen Funktionsmodelle es gibt?
Ich denke, funktionale Programmierung kann als eine Programmiermethode verstanden werden, die Funktionen als Hauptträger verwendet, um allgemeine Ausdrücke zu zerlegen und zu abstrahieren
. Welche Vorteile hat dies? Die Hauptpunkte sind wie folgt:
klarere Semantik, höhere Wiederverwendbarkeit, bessere Wartbarkeit, bessere Bereichsbeschränkung und weniger Nebenwirkungen. Das folgende Beispiel ist ein spezifischer funktionaler Ausdruck von
Javascript-Code
// jedes Wort im Array erster Buchstabe // Allgemeines Schreiben const arr = ['apple', 'pen', 'apple-pen'] for(const i in arr){ const c = arr[i][0]; arr[i] = c.toUpperCase() + arr[i].slice(1); console.log(arr); // Funktionale Schreibmethode - function UpperFirst(word) { return word[0].toUpperCase() + word.slice(1); Funktion wordToUpperCase(arr) { return arr.map(upperFirst); console.log(wordToUpperCase(['apple', 'pen', 'apple-pen'])); // Funktionale Schreibmethode 2 console.log(arr.map(['apple', 'pen', 'apple-pen'], word => word[0].toUpperCase() + word.slice(1))) ;
Wenn die Situation komplexer wird, treten beim Schreiben von Ausdrücken mehrere Probleme auf:
Die Bedeutung ist nicht offensichtlich, die Wartung wird allmählich schwierig, die Wiederverwendbarkeit ist schlecht, es wird mehr Code generiert und es werden viele Zwischenvariablen generiert. Die funktionale Programmierung löst die oben genannten Probleme gut. Beziehen Sie sich zunächst auf die funktionale Schreibmethode 1, bei der Funktionen mithilfe der Funktionskapselung zerlegt werden (die Granularität ist nicht eindeutig), sie in verschiedene Funktionen gekapselt werden und dann kombinierte Aufrufe verwendet werden, um den Zweck zu erreichen. Dadurch ist der Ausdruck klar und leicht zu pflegen, wiederzuverwenden und zu erweitern. Zweitens ersetzt Array.map mithilfe von Funktionen höherer Ordnung for...of für die Array-Durchquerung und reduziert so Zwischenvariablen und Operationen.
Der Hauptunterschied zwischen der funktionalen Schreibmethode 1 und der funktionalen Schreibmethode 2 besteht darin, dass Sie überlegen können, ob die Funktion später wiederverwendet werden kann. Wenn nicht, ist Letzteres besser.
Aus der oben beschriebenen funktionalen Schreibmethode 2 können wir ersehen, dass es beim Schreiben von funktionalem Code leicht ist, eine horizontale Erweiterung zu bewirken, d.
Javascript-Code
// Berechnen Sie die Summe der Zahlen // Allgemeine Schreibmethode console.log(1 + 2 + 3 - 4) // Funktionale Schreibfunktion sum(a, b) { return a + b; Funktion sub(a, b) { return a - b } console.log(sub(sum(sum(1, 2), 3), 4); Dieses Beispiel zeigt nur einen extremen Fall einer horizontalen Erweiterung. Da die Anzahl der verschachtelten Funktionsebenen weiter zunimmt, wird der Code weniger lesbar . Die Leistung wird stark reduziert und es kommt leicht zu Fehlern. In diesem Fall können wir mehrere Optimierungsmethoden in Betracht ziehen, beispielsweise die folgende Kettenoptimierung. // Optimieren Sie das Schreiben (naja, Sie haben richtig gelesen, das ist das Kettenschreiben von lodash) Javascript-Code const utils = { Kette(a) { this._temp = a; gib dies zurück; }, Summe(b) { this._temp += b; gib dies zurück; }, sub(b) { this._temp -= b; gib dies zurück; }, Wert() { const _temp = this._temp; this._temp = undefiniert; return _temp; } }; console.log(utils.chain(1).sum(2).sum(3).sub(4).value());
Nach dem Umschreiben auf diese Weise wird die Gesamtstruktur und jedes Glied der Kette klarer Was zu tun ist, kann auch einfach angezeigt werden. Ein weiteres gutes Beispiel für den Vergleich zwischen Funktionsverschachtelung und -verkettung ist die Callback-Funktion und das Promise-Muster.
Javascript-Code
// Zwei Schnittstellen nacheinander anfordern // Callback-Funktion import $ from 'jquery' $.post('a/url/to/target', (rs) => { if(rs){ $.post('a/url/to/another/target', (rs2) => { if(rs2){ $.post('a/url/to/third/target'); } }); } }); // Promise-Importanfrage von 'catta'; // catta ist ein einfaches Anfragetool, das Fetch, JSONP, Ajax unterstützt und keine Abhängigkeiten hat request('a/url/to/target') .then(rs => rs ? $.post('a/url/to/another/target') : Promise.reject()) .then(rs2 => rs2 ? $.post('a/url/to/third/target') : Promise.reject());
Mit zunehmender Verschachtelungsebene und einstufiger Komplexität wird es zu Es ist aufgebläht und schwer zu warten, aber die Kettenstruktur von Promise kann sich bei hoher Komplexität immer noch vertikal ausdehnen, und die hierarchische Isolation ist sehr klar.
Das gängige funktionale Programmiermodell
kann lokale Variablen in einem nicht freigegebenen Codeblock beibehalten.
Das Konzept des „Closure“ ist meiner Meinung nach relativ abstrakt und nutzt diese
Funktion Können uns Taschen bringen?
Schauen wir uns zunächst an, wie man einen Abschluss erstellt:
Javascript-Code
// Eine Abschlussfunktion erstellen makeCounter() { sei k = 0; Rückgabefunktion() { return ++k; }; } const counter = makeCounter(); console.log(counter()); // 1 console.log(counter()); // 2
makeCounter Der Codeblock dieser Funktion verweist auf die lokale Variable k in der zurückgegebenen Funktion, was dazu führt, dass die lokale Variable fehlschlägt Wenn die Funktion ausgeführt wird, wird sie vom System recycelt und erzeugt so einen Abschluss. Die Funktion dieses Abschlusses besteht darin, die lokale Variable zu „behalten“, sodass die Variable beim Aufruf der inneren Funktion wiederverwendet werden kann. Im Gegensatz zu globalen Variablen kann auf diese Variable nur innerhalb der Funktion verwiesen werden.
Mit anderen Worten: Abschlüsse erzeugen tatsächlich einige „persistente Variablen“, die für die Funktion privat sind.
Aus diesem Beispiel können wir schließen, dass die Bedingungen für die Erstellung eines Abschlusses folgende sind:
Es gibt innere und äußere Funktionen. Der Hauptzweck des Abschlusses besteht darin, ihn zu definieren Bei einigen Funktionen handelt es sich um domänenbegrenzte persistente Variablen. Diese Variablen können zum Zwischenspeichern oder für Zwischenberechnungen usw. verwendet werden.
Javascript-Code
// Einfaches Caching-Tool // Die anonyme Funktion erstellt einen Abschluss const Cache = (function() { const store = {}; zurückkehren { get(key) { return store[key]; }, set(key, val) { store[key] = val; } } }()); Cache.set('a', 1); Cache.get('a'); // 1Das
obige Beispiel ist die Implementierung eines einfachen Caching-Tools. Die anonyme Funktion erstellt einen Abschluss, sodass auf das Speicherobjekt immer verwiesen werden kann. , wird nicht recycelt.
Nachteile von Schließungen: Persistente Variablen werden nicht normal freigegeben und belegen weiterhin Speicherplatz, was leicht zu Speicherverschwendung führen kann, sodass im Allgemeinen einige zusätzliche manuelle Bereinigungsmechanismen erforderlich sind.
Funktion, die eine Funktion akzeptiert oder zurückgibt, wird als Funktion höherer Ordnung bezeichnet.
Es klingt wie ein sehr kaltes Wort, aber tatsächlich verwenden wir es oft, aber wir kennen ihre Namen nicht. Die JavaScript-Sprache unterstützt nativ Funktionen höherer Ordnung, da JavaScript-Funktionen erstklassige Bürger sind und sowohl als Parameter als auch als Rückgabewert einer anderen Funktion verwendet werden können.
Wir können oft viele native Funktionen höherer Ordnung in JavaScript sehen, wie zum
Beispiel Array.map, Array.reduce und Array.filter. Sehen wir uns an, wie er
Map (Mapping
Mit anderen Worten, jedes Element in der Menge wird derselben Transformation unterzogen, um eine neue Mengenzuordnung zu generieren
. Als Funktion höherer Ordnung akzeptiert sie einen Funktionsparameter als logischen
Javascript-Code
der Zuordnung// Fügen Sie zu jedem Element einen hinzu das Array, um ein neues Array zu bilden // Allgemeine Schreibmethode const arr = [1,2,3]; for(const n of arr){ rs.push(++n); } console.log(rs) // Map wird neu geschrieben const arr = [1,2,3]; const rs = arr.map(n => ++n);
Die Verwendung der for...of-Schleife zum Durchlaufen des Arrays führt dazu Zusätzliche Operationen und Es besteht das Risiko
, dass das ursprüngliche Array geändert wird. Die Kartenfunktion kapselt jedoch die erforderlichen Operationen, sodass wir uns nur um die Funktionsimplementierung der Zuordnungslogik kümmern müssen, wodurch die Codemenge und das Seitenrisiko verringert werden Effekte.
gibt einige Parameter einer Funktion an und generiert eine neue Funktion, die andere Parameter akzeptiert.
Sie hören diesen Begriff vielleicht nicht oft, aber jeder, der Undescore oder Lodash verwendet hat, hat ihn gesehen.
Es gibt eine magische _.partial-Funktion, dieJavascript-Code
enthält
// Den relativen Pfad der Zieldatei zum Basispfad abrufen // Die allgemeine Schreibmethode ist const BASE = '/path/to/base'; path. relative(BASE, '/some/path'); // _.parical rewrite const BASE = '/path/to/base'; const relativeFromBase = _.partial(path.relative, BASE); const relativePath = relativeFromBase('/some/path');
Durch _.partial erhalten wir die neue Funktion relativeFromBase. Wenn diese Funktion aufgerufen wird, entspricht sie dem Aufruf von path.relative, und der erste Parameter wird standardmäßig an BASE übergeben Nachfolgende Parameter werden der Reihe nach angehängt.
In diesem Fall möchten wir wirklich erreichen, dass der Pfad jedes Mal relativ zu BASE und nicht relativ zu irgendeinem Pfad ermittelt wird. Durch Currying können wir uns nur um einige Parameter einer Funktion kümmern, wodurch der Zweck der Funktion klarer wird und der Aufruf einfacher wird.
werden die Fähigkeiten mehrerer Funktionen kombiniert, um eine neue Funktion zu erstellen.
Möglicherweise haben Sie es zum ersten Mal in lodash gesehen, der Compose-Methode (jetzt Flow genannt).
Javascript-Code
// Jedes Wort im Array groß schreiben, Base64 ausführen //Allgemeine Schreibmethode (eine davon) const arr = ['pen', 'apple', 'applypen']; const rs = []; rs.push(btoa(w.toUpperCase())); } console.log(rs); // _.flow rewrite const arr = ['pen', 'apple', 'applypen']; const UpperAndBase64 = _.partialRight(_.map, _.flow(_.upperCase, btoa)); console.log(upperAndBase64(arr));
_.flow führt die Funktionen der Großbuchstabenkonvertierung und der Base64-Konvertierungsfunktionen zusammen, um eine neue Funktion zu generieren. Bequem als Parameterfunktion oder spätere Wiederverwendung zu verwenden.
Aus meiner Sicht unterscheidet sich mein Verständnis der funktionalen JavaScript-Programmierung möglicherweise von vielen traditionellen Konzepten. Ich denke nicht nur, dass Funktionen höherer Ordnung zur funktionalen Programmierung zählen. Andere, wie zum Beispiel kombinierte Aufrufe gewöhnlicher Funktionen, Kettenstrukturen usw., gehören meiner Meinung nach zur Kategorie der funktionalen Programmierung, solange sie Funktionen als Hauptfunktionen verwenden Träger.
Und ich denke, funktionale Programmierung ist weder notwendig noch sollte sie eine verbindliche Regel oder Anforderung sein. Wie objektorientierte oder andere Ideen ist es auch eine dieser Möglichkeiten. In den meisten Fällen sollten wir eine Kombination aus mehreren sein und uns nicht auf Konzepte beschränken.
Verwandte Empfehlungen: JavaScript-Tutorial.
Das Obige ist eine detaillierte Diskussion der JavaScript-Funktionsprogrammierung. Weitere Informationen finden Sie in anderen verwandten Artikeln auf der chinesischen PHP-Website.