Ich habe eine Menge react
-Code gelesen und sehe solche Dinge, die ich nicht verstehe:
handleChange = field => e => {
e.preventDefault();
/// Do something here
}
Das ist ein Curry-Funktion
Untersuchen Sie zunächst diese Funktion mit zwei Parametern…
const add = (x, y) => x + y
add(2, 3) //=> 5
Hier ist es wieder in Curryform ...
const add = x => y => x + y
Hier ist das gleiche1 Code ohne Pfeilfunktionen…
const add = function (x) {
return function (y) {
return x + y
}
}
Fokus auf return
Es könnte hilfreich sein, es auf andere Weise zu visualisieren. Wir wissen, dass Pfeilfunktionen wie folgt funktionieren - achten wir besonders auf den Rückgabewert .
const f = someParam => returnValue
Unsere add
-Funktion gibt also eine Funktion zurück - wir können Klammern verwenden, um die Übersichtlichkeit zu erhöhen. Der fettgedruckte Text ist der Rückgabewert unserer Funktion add
const add = x => (y => x + y)
Mit anderen Worten, add
einer bestimmten Zahl gibt eine Funktion zurück
add(2) // returns (y => 2 + y)
Aufrufen von Curry-Funktionen
Um unsere Curry-Funktion nutzen zu können, müssen wir sie etwas anders nennen ...
add(2)(3) // returns 5
Dies liegt daran, dass der erste (äußere) Funktionsaufruf eine zweite (innere) Funktion zurückgibt. Erst nach dem Aufruf der zweiten Funktion erhalten wir das Ergebnis. Dies wird deutlicher, wenn wir die Anrufe auf zwei Leitungen trennen ...
const add2 = add(2) // returns function(y) { return 2 + y }
add2(3) // returns 5
Anwenden unseres neuen Verständnisses auf Ihren Code
related: ”Was ist der Unterschied zwischen Bindung, Teilanmeldung und Currying?”
OK, jetzt, da wir verstehen, wie das funktioniert, schauen wir uns Ihren Code an
handleChange = field => e => {
e.preventDefault()
/// Do something here
}
Wir beginnen mit der Darstellung ohne Verwendung von Pfeilfunktionen…
handleChange = function(field) {
return function(e) {
e.preventDefault()
// Do something here
// return ...
};
};
Da Pfeilfunktionen jedoch lexikalisch this
binden, würde tatsächlich ungefähr so aussehen ...
handleChange = function(field) {
return function(e) {
e.preventDefault()
// Do something here
// return ...
}.bind(this)
}.bind(this)
Vielleicht können wir jetzt klarer sehen, was dies bewirkt. Die Funktion handleChange
erstellt eine Funktion für ein angegebenes field
. Dies ist eine praktische React - Technik, da Sie für jeden Eingang Ihre eigenen Listener einrichten müssen, um den Status Ihrer Anwendungen zu aktualisieren. Mit der Funktion handleChange
können wir dies beseitigen Der gesamte duplizierte Code, der dazu führen würde, dass change
Listener für jedes Feld eingerichtet werden.
1 Hier musste ich this
nicht lexikalisch binden, da die ursprüngliche Funktion add
keinen Kontext verwendet. Daher ist es in diesem Fall nicht wichtig, ihn beizubehalten.
Noch mehr Pfeile
Bei Bedarf können mehr als zwei Pfeilfunktionen sequenziert werden -
const three = a => b => c =>
a + b + c
const four = a => b => c => d =>
a + b + c + d
three (1) (2) (3) // 6
four (1) (2) (3) (4) // 10
Curry-Funktionen können überraschende Dinge bewirken. Nachfolgend sehen wir $
Als eine Curry-Funktion mit zwei Parametern definiert. Am Aufrufstandort sieht es jedoch so aus, als könnten wir eine beliebige Anzahl von Argumenten angeben. Currying ist die Abstraktion von Arität -
const $ = x => k =>
$ (k (x))
const add = x => y =>
x + y
const mult = x => y =>
x * y
$ (1) // 1
(add (2)) // + 2 = 3
(mult (6)) // * 6 = 18
(console.log) // 18
$ (7) // 7
(add (1)) // + 1 = 8
(mult (8)) // * 8 = 64
(mult (2)) // * 2 = 128
(mult (2)) // * 2 = 256
(console.log) // 256
Teilanwendung
Teilanwendung ist ein verwandtes Konzept. Es erlaubt uns, Funktionen teilweise anzuwenden, ähnlich wie beim Currying, außer dass die Funktion nicht in Currying-Form definiert werden muss.
const partial = (f, ...a) => (...b) =>
f (...a, ...b)
const add3 = (x, y, z) =>
x + y + z
partial (add3) (1, 2, 3) // 6
partial (add3, 1) (2, 3) // 6
partial (add3, 1, 2) (3) // 6
partial (add3, 1, 2, 3) () // 6
partial (add3, 1, 1, 1, 1) (1, 1, 1, 1, 1) // 3
Hier ist eine Demo von partial
, mit der Sie in Ihrem eigenen Browser spielen können -
const partial = (f, ...a) => (...b) =>
f (...a, ...b)
const preventDefault = (f, event) =>
( event .preventDefault ()
, f (event)
)
const logKeypress = event =>
console .log (event.which)
document
.querySelector ('input[name=foo]')
.addEventListener ('keydown', partial (preventDefault, logKeypress))
<input name="foo" placeholder="type here to see ascii codes" size="50">
Wenn Sie die verfügbaren Syntaxen für Pfeilfunktionen verstehen, werden Sie verstehen, welches Verhalten sie bewirken, wenn sie wie in den von Ihnen angegebenen Beispielen verkettet sind.
Wenn eine Pfeilfunktion ohne blockierte Klammern mit oder ohne mehrere Parameter geschrieben wird, wird der Ausdruck, der den Rumpf der Funktion ausmacht, implizit zurückgegeben. In Ihrem Beispiel ist dieser Ausdruck eine andere Pfeilfunktion.
No arrow funcs Implicitly return `e=>{…}` Explicitly return `e=>{…}`
---------------------------------------------------------------------------------
function (field) { | field => e => { | field => {
return function (e) { | | return e => {
e.preventDefault() | e.preventDefault() | e.preventDefault()
} | | }
} | } | }
Ein weiterer Vorteil des Schreibens anonymer Funktionen mit der Pfeilsyntax besteht darin, dass sie an den Gültigkeitsbereich gebunden sind, in dem sie definiert sind. Von 'Pfeilfunktionen' in MDN :
Ein arrow-Funktionsausdruck hat im Vergleich zu Funktionsausdrücke eine kürzere Syntax und bindet den this -Wert lexikalisch. Pfeilfunktionen sind immer anonym .
Dies ist in Ihrem Beispiel besonders relevant, da es aus einer reactjs -Anwendung stammt. Wie von @naomik ausgeführt, greifen Sie in React häufig auf Member-Funktionen der Komponente mit this
zu. Zum Beispiel:
Unbound Explicitly bound Implicitly bound
------------------------------------------------------------------------------
function (field) { | function (field) { | field => e => {
return function (e) { | return function (e) { |
this.setState(...) | this.setState(...) | this.setState(...)
} | }.bind(this) |
} | }.bind(this) | }
Ein allgemeiner Tipp: Wenn Sie durch die neue JS-Syntax und deren Kompilierung verwirrt werden, können Sie babel überprüfen. Wenn Sie beispielsweise Ihren Code in Babel kopieren und die Voreinstellung für es2015 auswählen, erhalten Sie eine solche Ausgabe
handleChange = function handleChange(field) {
return function (e) {
e.preventDefault();
// Do something here
};
};
Stellen Sie sich das so vor: Jedes Mal, wenn Sie einen Pfeil sehen, ersetzen Sie ihn durch function
.function parameters
ist vor dem Pfeil definiert.
In Ihrem Beispiel also:
field => // function(field){}
e => { e.preventDefault(); } // function(e){e.preventDefault();}
und dann zusammen:
function (field) {
return function (e) {
e.preventDefault();
};
}
// Basic syntax:
(param1, param2, paramN) => { statements }
(param1, param2, paramN) => expression
// equivalent to: => { return expression; }
// Parentheses are optional when there's only one argument:
singleParam => { statements }
singleParam => expression
Kurz und einfach ????
Es ist eine Funktion, die eine andere, kurz geschriebene Funktion zurückgibt.
const handleChange = field => e => {
e.preventDefault()
// Do something here
}
// is equal to
function handleChange(field) {
return function(e) {
e.preventDefault()
// Do something here
}
}
Warum Leute das machen ❓
Haben Sie es mit dem Schreiben einer Funktion zu tun gehabt, die Sie anpassen können? Oder Sie müssen eine Callback-Funktion schreiben, die über feste Parameter (Argumente), Verfügt. Sie müssen jedoch mehr Variablen an die Funktion übergeben, aber die globalen Werte vermeiden variables? Wenn Ihre Antwort "yes" ist, dann ist es die Art und Weise, wie es geht.
Zum Beispiel haben wir eine button
mit onClick Callback. Und wir müssen id
an die Funktion übergeben, aber onClick
akzeptiert nur einen Parameter event
, wir können keine zusätzlichen Parameter innerhalb dieser Funktion übergeben:
const handleClick = (event, id) {
event.preventDefault()
// Dispatch some delete action by passing record id
}
Es wird nicht funktionieren!
Deshalb erstellen wir eine Funktion, die andere Funktionen mit ihrem eigenen Bereich von Variablen ohne globale Variablen zurückgibt, da globale Variablen schlecht sind.
Unterhalb der Funktion handleClick(props.id)}
wird eine Funktion aufgerufen und es wird id
in ihrem Gültigkeitsbereich angezeigt! Egal wie oft gedrückt wird, die IDs beeinflussen oder verändern sich nicht, sie sind völlig isoliert.
const handleClick = id => event {
event.preventDefault()
// Dispatch some delete action by passing record id
}
const Confirm = props => (
<div>
<h1>Are you sure to delete?</h1>
<button onClick={handleClick(props.id)}>
Delete
</button>
</div
)
Das Beispiel in Ihrer Frage ist das eines curried function
, der arrow function
verwendet und für das erste Argument einen implicit return
hat.
Die Pfeilfunktion bindet dies lexikalisch, d. H. Sie haben kein eigenes this
-Argument, sondern nehmen den this
-Wert aus dem einschließenden Bereich
Ein Äquivalent des obigen Codes wäre
const handleChange = (field) {
return function(e) {
e.preventDefault();
/// Do something here
}.bind(this);
}.bind(this);
Zu Ihrem Beispiel ist noch Folgendes zu beachten: Definieren Sie handleChange
als const oder Funktion. Wahrscheinlich verwenden Sie es als Teil einer Klassenmethode und es verwendet einen class fields syntax
anstatt die äußere Funktion direkt zu binden, würden Sie sie im Klassenkonstruktor binden
class Something{
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
}
handleChange(field) {
return function(e) {
e.preventDefault();
// do something
}
}
}
Eine andere Sache, die im Beispiel zu beachten ist, ist der Unterschied zwischen impliziter und expliziter Rückgabe.
const abc = (field) => field * 2;
Oben ist ein Beispiel für die implizite Rückkehr, dh. Es nimmt das Wertfeld als Argument und gibt das Ergebnis field*2
zurück, das die zurückzugebende Funktion explizit angibt
Für eine explizite Rückgabe würden Sie die Methode explizit anweisen, den Wert zurückzugeben
const abc = () => { return field*2; }
Zu Pfeilfunktionen ist außerdem zu beachten, dass sie keine eigene arguments
haben, sondern diese auch vom übergeordneten Bereich übernehmen.
Zum Beispiel, wenn Sie einfach eine Pfeilfunktion definieren
const handleChange = () => {
console.log(arguments) // would give an error on running since arguments in undefined
}
Als Alternative stellen Pfeilfunktionen die Restparameter bereit, die Sie verwenden können
const handleChange = (...args) => {
console.log(args);
}
var handleChange = field => e => {
e.preventDefault();
/// Do something here
}
In Ecma5 das Übersetzen:
"use strict";
var handleChange = function handleChange(field) {
return function (e) {
e.preventDefault(); /// Do something here
};
};
var f = function(x,y) { return x+y }
var g = function(x) { return function(y) { return x+y }}
f: (T x T) -> T
g: T -> T -> T
T: generischer Typ
Es ändert sich der Typ der Funktion, aber das Ergebnis nein.