it-swarm.com.de

Was bedeuten mehrere Pfeilfunktionen in Javascript?

Ich habe eine Menge react-Code gelesen und sehe solche Dinge, die ich nicht verstehe:

handleChange = field => e => {
  e.preventDefault();
  /// Do something here
}
301
jhamm

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">
640
user633183

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)             |  }
48
sdgluck

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
   };
 };

babel

39
Rahil Ahmad

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(); 
    };
}

Aus den Dokumenten :

// 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
32
LifeQuery

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
)

2
sultan

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);
}
1
Shubham Khatri
 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.

0
koλzar