Front-End-JavaScript versteht Funktions-Currying gründlich

Front-End-JavaScript versteht Funktions-Currying gründlich

1. Was ist Curry

In der Mathematik und Informatik ist Currying eine Technik zum Umwandeln einer Funktion mit mehreren Argumenten in eine Reihe von Funktionen mit jeweils einem einzelnen Argument.

Beispielsweise nimmt eine normale Funktion, die drei Parameter annimmt, nach dem Currying die Curry-Version der Funktion einen Parameter an und gibt eine Funktion zurück, die den nächsten Parameter annimmt, die wiederum eine Funktion zurückgibt, die den dritten Parameter annimmt. Nach dem Erhalt des dritten Parameters wendet die letzte Funktion die drei zuvor erhaltenen Parameter auf die ursprüngliche normale Funktion an und gibt das Endergebnis zurück.

Currying in Mathematik und Informatik:

// Currying in Mathematik und Informatik:

//Eine normale Funktion, die drei Parameter empfängt function sum(a,b,c) {
    konsole.log(a+b+c)
}

//Eine Tool-Funktion zum Konvertieren einer normalen Funktion in eine Curry-Version function curry(fn) {
  //... interne Implementierung ausgelassen, gib eine neue Funktion zurück }

//Eine Curry-Funktion abrufen let _sum = curry(sum);

//Gib eine Funktion zurück, die den zweiten Parameter empfängt let A = _sum(1);
//Gib eine Funktion zurück, die den dritten Parameter empfängt let B = A(2);
//Erhalte den letzten Parameter, wende alle vorherigen Parameter auf die ursprüngliche Funktion an und führe B(3) aus // print : 6

Für die Sprache Javascript ist das Konzept der Currying-Funktion, über das wir normalerweise sprechen, nicht genau dasselbe wie das Konzept des Currying in der Mathematik und Informatik.

In der Mathematik und Informatik kann einer Curry-Funktion immer nur ein Argument auf einmal übergeben werden;

Die Curry-Funktionen in unseren eigentlichen Javascript Anwendungen können einen oder mehrere Parameter übergeben.

Schauen wir uns dieses Beispiel an:

//Gewöhnliche Funktion function fn(a,b,c,d,e) {
  konsole.log(a,b,c,d,e)
}
//Generierte Curry-Funktion let _fn = curry(fn);

_fn(1,2,3,4,5); // drucken: 1,2,3,4,5
_fn(1)(2)(3,4,5); // drucken: 1,2,3,4,5
_fn(1,2)(3,4)(5); // drucken: 1,2,3,4,5
_fn(1)(2)(3)(4)(5); // drucken: 1,2,3,4,5

Bei der Curry-Funktion _fn gilt: Wenn die Anzahl der empfangenen Parameter mit der Anzahl der formalen Parameter der ursprünglichen Funktion übereinstimmt, wird die ursprüngliche Funktion ausgeführt. Wenn die Anzahl der empfangenen Parameter kleiner ist als die Anzahl der formalen Parameter der ursprünglichen Funktion, wird eine Funktion zurückgegeben, um die verbleibenden Parameter zu empfangen, bis die Anzahl der empfangenen Parameter mit der Anzahl der formalen Parameter übereinstimmt. Dann wird die ursprüngliche Funktion ausgeführt.

Nachdem wir nun wissen, was Currying ist, schauen wir uns an, wofür Currying verwendet wird.

2. Verwendung von Curry

Currying erschwert zwar die einfache Antwort, gleichzeitig haben wir aber mehr Freiheit bei der Verwendung von Funktionen. Der freie Umgang mit Funktionsparametern ist hierbei der Kern des Curryings. Der Sinn des Curryings liegt darin, die Allgemeingültigkeit zu reduzieren und die Anwendbarkeit zu erhöhen. Schauen wir uns ein Beispiel an:

Bei unserer Arbeit werden wir auf verschiedene Anforderungen stoßen, die eine Überprüfung mittels regulärer Ausdrücke erfordern, wie etwa die Überprüfung von Telefonnummern, E-Mail-Adressen, ID-Nummern, Passwörtern usw. Zu diesem Zeitpunkt werden wir eine allgemeine Funktion checkByRegExp kapseln, die zwei Parameter empfängt, das zu überprüfende reguläre Ausdrucksobjekt und die zu überprüfende Zeichenfolge.

Funktion checkByRegExp(regExp,Zeichenfolge) {
    gibt regExp.test(Zeichenfolge) zurück;  
}

checkByRegExp(/^1\d{10}$/, '18642838455'); // Telefonnummer prüfen checkByRegExp(/^(\w)+(\.\w+)*@(\w)+((\.\w+)+)$/, '[email protected]'); // E-Mail-Adresse prüfen

Auf den ersten Blick ist der obige Code in Ordnung und kann alle unsere Anforderungen zum Bestehen von Tests mit regulären Ausdrücken erfüllen. Aber lassen Sie uns über folgende Frage nachdenken: Was wäre, wenn wir mehrere Telefonnummern oder mehrere E-Mail-Adressen verifizieren müssten?

Wir könnten Folgendes tun:

:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::3s::::::333:33333333333333333333ag33333333333333333333333333333 es333333333333333333333333333333 es33 nicht nicht nicht nicht nicht nicht nicht nicht nicht nicht nicht nicht nicht dann dann dann dann aber33333333333333333333 nicht3 nicht3 nicht3 nicht3 nicht3 nicht3 nicht3 nicht3 dann3 dann3 aber3 dann3 nicht nicht nicht nicht nicht nicht nicht nicht nicht nicht nicht nicht nicht nicht nicht nicht nicht nicht nicht nicht nicht nicht nicht nicht nicht nicht nicht nicht nicht nicht nicht nicht dann nichtie dasen aber aber abersossoss aberstens aberstensss aberten aber abers :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::3s::::::333:33333333333333333333ag33333333333333333333333333333 es333333333333333333333333333333 es33 nicht nicht nicht nicht nicht nicht nicht nicht nicht nicht nicht nicht nicht dann dann dann dann aber33333333333333333333 nicht3 nicht3 nicht3 nicht3 nicht3 nicht3 nicht3 nicht3 dann3 dann3 aber3 dann3 nicht nicht nicht nicht nicht nicht nicht nicht nicht nicht nicht nicht nicht nicht nicht nicht nicht nicht nicht nicht nicht nicht nicht nicht nicht nicht nicht nicht nicht nicht nicht nicht dann nichtie dasen aber aber abersossoss aberstens aberstensss aberten aber abers :::::::::::

Bei jeder Prüfung müssen wir eine Zeichenfolge mit regulären Ausdrücken eingeben. Wenn wir denselben Datentyp prüfen, müssen wir denselben regulären Ausdruck mehrmals schreiben, was seine Verwendung ineffizient macht. Und da die Funktion checkByRegExp selbst eine Tool-Funktion ist und keine Bedeutung hat, müssen wir, wenn wir uns diese Codes nach einer Weile erneut ansehen und keine Kommentare vorhanden sind, den Inhalt des regulären Ausdrucks prüfen, um zu wissen, ob wir eine Telefonnummer, eine E-Mail-Adresse oder etwas anderes prüfen.

An diesem Punkt können wir Currying verwenden, um die Funktion checkByRegExp zu kapseln, um das Schreiben des Codes zu vereinfachen und die Lesbarkeit des Codes zu verbessern.

//Curry durchführen let _check = curry(checkByRegExp);
//Toolfunktion zum Überprüfen der Telefonnummer generieren let checkCellPhone = _check(/^1\d{10}$/);
//Toolfunktion zum Überprüfen von E-Mails generieren checkEmail = _check(/^(\w)+(\.\w+)*@(\w)+((\.\w+)+)$/);

checkCellPhone('18642838455'); // Telefonnummer bestätigencheckCellPhone('13109840560'); // Telefonnummer bestätigencheckCellPhone('13204061212'); // Telefonnummer bestätigencheckEmail('[email protected]'); // E-Mail bestätigencheckEmail('[email protected]'); // E-Mail bestätigencheckEmail('[email protected]'); // E-Mail bestätigen

Mal sehen, ob unser Code nach der Curry-Kapselung prägnant und intuitiv wird.

Nach dem Currying haben wir zwei Funktionen generiert checkCellPhone 和checkEmail, checkCellPhone kann nur überprüfen, ob die übergebene Zeichenfolge eine Telefonnummer ist, und die Funktion checkEmail kann nur überprüfen, ob die übergebene Zeichenfolge eine E-Mail-Adresse ist. Im Vergleich zur ursprünglichen Funktion checkByRegExp ist ihre funktionale Vielseitigkeit reduziert, ihre Anwendbarkeit jedoch verbessert. Diese Verwendung von Currying kann wie folgt verstanden werden: Wiederverwendung von Parametern

Schauen wir uns ein weiteres Beispiel an

Angenommen, wir haben solche Daten:

lass Liste = [
    {
        Name: „Lucy“
    },
    {
        Name: „Jack“
    }
]

Wir müssen die Werte aller Namensattribute in den Daten abrufen. Normalerweise würden wir Folgendes tun:

let Namen = Liste.map(Funktion(Element) {
  Artikelname zurückgeben;
})

Wie setzen wir dies also mit Curry-Denken um?

let prop = curry(Funktion(Schlüssel,Objekt) {
    returniere Objekt[Schlüssel];
})
let Namen = Liste.map(Eigenschaft('Name'))

Wenn Sie dies sehen, haben Sie möglicherweise Fragen. Warum müssen wir für ein so einfaches Beispiel, nur um den Attributwert von name zu erhalten, eine prop -Funktion implementieren? Das ist zu mühsam.

Wir können unsere Denkweise ändern. Nachdem die prop Funktion einmal implementiert wurde, kann sie in Zukunft mehrfach verwendet werden. Wenn wir also die Komplexität des Codes berücksichtigen, können wir die Implementierung prop -Funktion entfernen.

Unser eigentlicher Code kann als nur eine Zeile verstanden werden let names = list.map(prop('name'))

Durch Currying ist unser Code also prägnanter und lesbarer geworden.

3. So kapseln Sie Curry-Hilfsfunktionen

Als nächstes überlegen wir, wie die curry -Funktion implementiert wird.

Erinnern Sie sich an unsere vorherige Definition von Currying, das einige Parameter akzeptiert, eine Funktion zurückgibt, um die verbleibenden Parameter zu akzeptieren, und die ursprüngliche Funktion ausführt, nachdem genügend Parameter empfangen wurden.

Wir wissen bereits, dass die ursprüngliche Funktion ausgeführt wird, wenn die Curry-Funktion genügend Parameter erhält. Wie bestimmen wir also, wann genügend Parameter erreicht sind?

Wir verfolgen zwei Ansätze:

  1. Ermitteln Sie die Anzahl der formalen Parameter der Funktion über die Längeneigenschaft der Funktion. Die Anzahl der formalen Parameter ist die Anzahl der erforderlichen Parameter.
  2. Geben Sie die Anzahl der erforderlichen Argumente beim Aufruf der Curry-Hilfsfunktion manuell an

Wir kombinieren diese beiden Punkte, um eine einfache curry -Funktion zu implementieren:

/**
 * Curry die Funktion * @param fn die ursprüngliche Funktion, die curryed werden soll * @param len die Anzahl der benötigten Parameter, standardmäßig die Anzahl der formalen Parameter der ursprünglichen Funktion */
Funktion Curry (fn, Länge = fn. Länge) {
    returniere _curry.call(diese,fn,länge)
}

/**
 * Übertragungsfunktion * @param fn ursprüngliche Funktion, die ausgeführt werden soll * @param len benötigte Anzahl Parameter * @param args empfangene Parameterliste */
Funktion _curry(fn,Länge,...Argumente) {
    Rückgabefunktion (...params) {
        let _args = [...args,...params];
        if(_args.length >= len){
            gibt fn.apply(diese,_args) zurück;
        }anders{
            returniere _curry.call(diese,fn,länge,..._args)
        }
    }
}

Lassen Sie uns dies überprüfen:

lass _fn = curry(Funktion(a,b,c,d,e){
    konsole.log(a,b,c,d,e)
});

_fn(1,2,3,4,5); // drucken: 1,2,3,4,5
_fn(1)(2)(3,4,5); // drucken: 1,2,3,4,5
_fn(1,2)(3,4)(5); // drucken: 1,2,3,4,5
_fn(1)(2)(3)(4)(5); // drucken: 1,2,3,4,5

Unsere häufig verwendete Toolbibliothek lodash bietet auch eine curry -Methode und fügt eine sehr interessante placeholder hinzu, die die Reihenfolge der eingehenden Parameter mithilfe von Platzhaltern ändert.

Wenn wir beispielsweise einen Platzhalter übergeben, überspringen die in diesem Aufruf übergebenen Parameter den Platzhalter und der Platzhalter wird mit den Parametern des nächsten Aufrufs gefüllt, wie folgt:

Schauen Sie sich das Beispiel der offiziellen Website an:

Als nächstes überlegen wir, wie die Platzhalterfunktion implementiert wird.

Für die curry -Funktion von lodash wird curry Funktion auf dem lodash Objekt bereitgestellt, sodass lodash Objekt als Standardplatzhalter verwendet wird.

Da die von uns selbst implementierte curry -Funktion auf keinem Objekt gemountet ist, verwenden wir curry -Funktion als Standardplatzhalter.

Der Zweck der Verwendung von Platzhaltern besteht darin, die Reihenfolge zu ändern, in der Parameter übergeben werden. Daher muss bei der Implementierung der curry -Funktion jedes Mal aufgezeichnet werden, ob ein Platzhalter verwendet wird und welche Parameterposition durch den Platzhalter dargestellt wird.

Direkt zum Code:

/**
 * @param fn die Funktion, die curryiert werden soll* @param length die Anzahl der erforderlichen Parameter, standardmäßig die Anzahl der formalen Parameter der Funktion* @param holder Platzhalter, standardmäßig die aktuelle curryierte Funktion* @return {Function} die Funktion nach dem Curry*/
Funktion Curry (fn, Länge = fn. Länge, Halter = Curry) {
    returniere _curry.call(diese,fn,Länge,Halter,[],[])
}
/**
 * Übertragungsfunktion* @param fn ursprüngliche Funktion des Curryings* @param length Anzahl der von der ursprünglichen Funktion benötigten Parameter* @param holder empfangener Platzhalter* @param args empfangene Parameterliste* @param holders empfangene Platzhalterpositionsliste* @return {Function} Funktion zum Fortsetzen des Curryings oder Endergebnis*/
Funktion _curry(fn,Länge,Inhaber,Argumente,Inhaber){
    Rückgabefunktion (..._args) {
        //Kopieren Sie die Parameter, um Verwirrung durch mehrere Operationen an derselben Funktion zu vermeiden. let params = args.slice();
        //Kopieren Sie die Platzhalterpositionsliste und fügen Sie die neu hinzugefügten Platzhalter hier ein let _holders = holders.slice();
        //Parameter durchlaufen, Parameter anhängen oder placeholders_args.forEach((arg,i)=>{ ersetzen
            //Vor dem eigentlichen Parameter steht ein Platzhalter. Ersetzen Sie den Platzhalter durch den eigentlichen Parameter if (arg !== holder && holders.length) {
                let index = Inhaber.Shift();
                _holders.splice(_holders.indexOf(index),1);
                Parameter[Index] = Argument;
            }
            //Vor dem eigentlichen Parameter steht kein Platzhalter. Füge den Parameter an die Parameterliste an, sonst wenn (arg !== holder && !holders.length) {
                Parameter.push(arg);
            }
            //Der Platzhalter wird übergeben. Wenn vorher kein Platzhalter vorhanden ist, notieren Sie die Position des Platzhalters, sonst wenn (arg === holder && !holders.length) {
                Parameter.push(arg);
                _holders.push(Parameterlänge - 1);
            }
            //Der übergebene Platzhalter. Davor steht ein Platzhalter. Lösche die ursprüngliche Position des Platzhalters. Sonst wenn (arg === holder && holders.length) {
                Inhaber.Shift();
            }
        });
        // Die ersten Längendatensätze in Parametern enthalten keine Platzhalter, führen Sie die Funktion aus, wenn (params.length >= length && params.slice(0,length).every(i=>i!==holder)){
            gibt fn.apply(diese, Parameter) zurück;
        }anders{
            returniere _curry.call(dies,fn,Länge,Inhaber,Parameter,_Inhaber)
        }
    }
}

Überprüfen Sie es:

sei fn = Funktion(a, b, c, d, e) {
    Konsole.log([a, b, c, d, e]);
}

let _ = {}; // Platzhalter definieren let _fn = curry(fn,5,_); // Curry die Funktion, spezifiziere die benötigte Anzahl Parameter, spezifiziere die benötigten Platzhalter _fn(1, 2, 3, 4, 5); // print: 1,2,3,4,5
_fn(_, 2, 3, 4, 5)(1); // drucken: 1,2,3,4,5
_fn(1, _, 3, 4, 5)(2); // drucken: 1,2,3,4,5
_fn(1, _, 3)(_, 4,_)(2)(5); // drucken: 1,2,3,4,5
_fn(1, _, _, 4)(_, 3)(2)(5); // drucken: 1,2,3,4,5
_fn(_, 2)(_, _, 4)(1)(3)(5); // drucken: 1,2,3,4,5

Wir haben eine Curry-Funktion vollständig implementiert~~

Damit ist dieser Artikel zum gründlichen Verständnis von Funktionscurrying in Front-End-JavaScript abgeschlossen. Weitere Informationen zum Funktionscurrying in JavaScript finden Sie in den vorherigen Artikeln von 123WORDPRESS.COM oder in den folgenden verwandten Artikeln. Ich hoffe, Sie werden 123WORDPRESS.COM auch in Zukunft unterstützen!

Das könnte Sie auch interessieren:
  • So implementieren Sie Funktions-Currying und -Decurrying in Javascript
  • Das Implementierungsprinzip und der Prozess des JavaScript-Funktionscurryings
  • Eine kurze Analyse der JavaScript-Funktion Currying
  • Analyse der Methoden und Beispiele für die Verwendung der JS-Funktion Currying
  • JavaScript implementiert Funktions-Currying und De-Currying-Prozessanalyse
  • Analyse des Prinzips und der Verwendung der JavaScript-Funktion Currying
  • Eine kurze Diskussion über Bind-Methoden und Funktions-Currying in JS
  • Eine kurze Analyse von Javascript-Closures und Function Currying
  • JavaScript-Funktion Currying erklärt
  • JavaScript-Funktion Currying

<<:  Detaillierte Erklärung der Bedeutung und des Unterschieds zwischen MySQL-Zeilensperren und Tabellensperren

>>:  Detaillierte Erklärung des gesamten Prozesses und der Schritte zur Installation von Clion auf Ubuntu16.04

Artikel empfehlen

Eine audiovisuelle Linux-Distribution, die Audiophile anspricht

Ich bin kürzlich auf das Audiovisual Linux Projec...

Tutorial zur Installation von Nginx in einer Linux-Umgebung

Inhaltsverzeichnis 1. Installieren Sie die erford...

Docker erstellt CMS-On-Demand-System mit Player-Funktion

Inhaltsverzeichnis Text 1. Maschine vorbereiten 2...

Gegenseitiger Wertetransfer und Aufruf von Vue-Eltern-Kind-Komponenten

Inhaltsverzeichnis 1. Übergeordnetes Element über...

Zwei Möglichkeiten zum Beenden von Bash im Docker-Container unter Linux

Wenn Sie Bash beenden möchten, haben Sie zwei Mög...

Drei Prinzipien effizienten Navigationsdesigns, die Webdesigner kennen müssen

Das Entwerfen der Navigation für eine Website ist...

HTML-Tabellen-Tag-Tutorial (7): Hintergrundfarbattribut BGCOLOR

Die Hintergrundfarbe der Tabelle kann über das At...

Bei der Verwendung von MySQL aufgetretene Probleme

Hier sind einige Probleme, die bei der Verwendung...

So aktivieren Sie die JMX-Überwachung über Tomcat

Erstellen Sie eine Simulationsumgebung: Betriebss...

Detaillierte Erklärung dieser Referenz in React

Inhaltsverzeichnis Ursache: durchlaufen: 1. Konst...