JavaScript-Pfeilfunktionen: Warum werden sie benötigt, wie werden sie behandelt, wann werden sie verwendet und wann nicht?

Eine der bemerkenswertesten Neuerungen des modernen JavaScript ist das Auftreten von Pfeilfunktionen, die manchmal als "fette" Pfeilfunktionen bezeichnet werden. Bei der Deklaration solcher Funktionen verwenden sie eine spezielle Zeichenkombination - => .

Pfeilfunktionen haben zwei Hauptvorteile gegenüber herkömmlichen Funktionen. Die erste ist eine sehr praktische und kompakte Syntax. Das zweite ist, dass der Ansatz, in Pfeilfunktionen damit zu arbeiten, intuitiver aussieht als in normalen Funktionen.

Bild

Manchmal führen diese und andere Vorteile dazu, dass der Pfeilsyntax eine bedingungslose Präferenz gegenüber anderen Arten der Deklaration von Funktionen eingeräumt wird. Beispielsweise erzwingt die beliebte Eslint-Konfiguration von Airbnb, dass eine solche Funktion bei jeder Erstellung einer anonymen Funktion pfeilartig ist.

Wie andere Konzepte und Mechanismen, die in der Programmierung verwendet werden, haben Pfeilfunktionen jedoch ihre Vor- und Nachteile. Ihre Verwendung kann negative Nebenwirkungen verursachen. Um die Pfeilfunktionen korrekt verwenden zu können, müssen Sie die damit verbundenen möglichen Probleme kennen.

Das Material, dessen Übersetzung wir heute veröffentlichen, wird sich auf die Funktionsweise von Pfeilfunktionen konzentrieren. Hier werden Situationen betrachtet, in denen ihre Verwendung den Code verbessern kann, und Situationen, in denen sie nicht verwendet werden sollten.

Verfügt über Pfeilfunktionen in JavaScript


Pfeilfunktionen in JavaScript sind so etwas wie Lambda-Funktionen in Python und Blöcke in Ruby.

Dies sind anonyme Funktionen mit einer speziellen Syntax, die eine feste Anzahl von Argumenten annehmen und im Kontext des Bereichs arbeiten, in dem sie enthalten sind, dh im Kontext der Funktion oder des anderen Codes, in dem sie deklariert sind.

Lassen Sie uns näher darauf eingehen.

▍ Syntaxpfeilfunktionen


Pfeilfunktionen werden nach einem einzigen Schema erstellt, während die Struktur von Funktionen in besonderen Fällen vereinfacht werden kann. Die Grundstruktur der Pfeilfunktion sieht folgendermaßen aus:

 (argument1, argument2, ... argumentN) => { //   } 

Die Liste der Funktionsargumente steht in Klammern, gefolgt von einem Pfeil aus = und > Zeichen. Anschließend wird der Hauptteil der Funktion in geschweiften Klammern angegeben.

Dies ist der Funktionsweise gewöhnlicher Funktionen sehr ähnlich. Die Hauptunterschiede bestehen darin, dass das function hier weggelassen wird und nach der Argumentliste ein Pfeil hinzugefügt wird.

In bestimmten Fällen können jedoch einfache Pfeilfunktionen mit viel kompakteren Konstruktionen deklariert werden.

Berücksichtigen Sie die Syntax, die verwendet wird, wenn der Hauptteil der Funktion durch einen einzelnen Ausdruck dargestellt wird. Sie können auf geschweifte Klammern verzichten, die den Funktionskörper umrahmen, und müssen die Ergebnisse der Auswertung eines Ausdrucks nicht mehr explizit zurückgeben, da dieses Ergebnis automatisch zurückgegeben wird. Zum Beispiel könnte es so aussehen:

 const add = (a, b) => a + b; 

Hier ist eine andere Variante der Kurzschreibweise der Funktion, die verwendet wird, wenn die Funktion nur ein Argument hat.

 const getFirst = array => array[0]; 

Wie Sie sehen können, werden die Klammern um die Argumentliste hier weggelassen. Darüber hinaus wird der Hauptteil der Funktion, der in diesem Beispiel durch einen einzelnen Befehl dargestellt wird, auch ohne Klammern geschrieben. Später werden wir mehr über die Vorteile solcher Designs sprechen.

▍Rückgabe von Objekten und Kurzaufzeichnungspfeilfunktionen


Bei der Arbeit mit Pfeilfunktionen werden auch einige komplexere Syntaxkonstrukte verwendet, die Sie beachten sollten.

Versuchen Sie beispielsweise, einen einzeiligen Ausdruck zu verwenden, um von einer Objektliteralfunktion zurückzukehren. Angesichts dessen, was wir bereits über Pfeilfunktionen wissen, scheint eine Funktionsdeklaration folgendermaßen auszusehen:

 (name, description) => {name: name, description: description}; 

Das Problem mit diesem Code ist seine Mehrdeutigkeit. Die geschweiften Klammern, mit denen wir das Objektliteral beschreiben möchten, sehen nämlich so aus, als würden wir versuchen, den Körper einer Funktion in sie einzuschließen.

Um dem System anzuzeigen, dass wir das Objektliteral meinen, müssen wir es in Klammern setzen:

 (name, description) => ({name: name, description: description}); 

▍ Pfeilfunktionen und deren Ausführungskontext


Im Gegensatz zu anderen Funktionen haben Pfeilfunktionen keinen eigenen Ausführungskontext .

In der Praxis bedeutet dies, dass sie die Entitäten this und die arguments von der übergeordneten Funktion erben.

Vergleichen Sie beispielsweise die beiden im folgenden Code dargestellten Funktionen. Einer von ihnen ist gewöhnlich, der zweite ist Pfeil.

 const test = { name: 'test object', createAnonFunction: function() {   return function() {     console.log(this.name);     console.log(arguments);   }; }, createArrowFunction: function() {   return () => {     console.log(this.name);     console.log(arguments);   }; } }; 

Es gibt ein test mit zwei Methoden. Jede von ihnen ist eine Funktion, die eine anonyme Funktion erstellt und zurückgibt. Der Unterschied zwischen diesen Methoden besteht nur darin, dass in der ersten der traditionelle funktionale Ausdruck und in der zweiten Pfeilfunktion verwendet wird.

Wenn wir mit diesem Code in der Konsole experimentieren und dieselben Argumente an die Methoden des Objekts übergeben, erhalten wir unterschiedliche Ergebnisse, obwohl die Methoden sehr ähnlich aussehen:

 > const anon = test.createAnonFunction('hello', 'world'); > const arrow = test.createArrowFunction('hello', 'world'); > anon(); undefined {} > arrow(); test object { '0': 'hello', '1': 'world' } 

Eine anonyme Funktion hat einen eigenen Kontext. Wenn sie aufgerufen wird und test.name , wird der Wert der Eigenschaft name des Objekts nicht test.name . Wenn Sie arguments aufrufen, wird die Liste der Argumente für die Funktion, die zum Erstellen und Zurückgeben der zu untersuchenden Funktion verwendet wurde, nicht angezeigt.

Im Fall einer Pfeilfunktion stellt sich heraus, dass ihr Kontext mit dem Kontext der Funktion übereinstimmt, die sie erstellt hat, wodurch sie sowohl auf die Liste der von dieser Funktion übergebenen Argumente als auch auf die name Eigenschaft des Objekts zugreifen kann, für das diese Funktion eine Methode ist.

Situationen, in denen Pfeilfunktionen den Code verbessern


▍Verarbeitungslisten von Werten


Herkömmliche Lambda-Funktionen sowie Pfeilfunktionen, nachdem sie in JavaScript angezeigt wurden, werden normalerweise in Situationen verwendet, in denen eine bestimmte Funktion auf jedes Element einer bestimmten Liste angewendet wird.

Wenn beispielsweise ein Array von Werten vorhanden ist, die mit der map Arrays-Methode konvertiert werden müssen, ist eine Pfeilfunktion ideal, um eine solche Konvertierung zu beschreiben:

 const words = ['hello', 'WORLD', 'Whatever']; const downcasedWords = words.map(word => word.toLowerCase()); 

Hier ist ein äußerst häufiges Beispiel für die ähnliche Verwendung von Pfeilfunktionen, die darin besteht, mit den Eigenschaften von Objekten zu arbeiten:

 const names = objects.map(object => object.name); 

Wenn sie anstelle herkömmlicher for Schleifen moderne iteratorbasierte forEach Schleifen verwenden, wird diese Verwendung durch die forEach this übergeordneten Entität intuitiv:

 this.examples.forEach(example => { this.runExample(example); }); 

▍ Versprechen und Versprechen Ketten


Eine andere Situation, in der Sie mit Pfeilfunktionen saubereren und verständlicheren Code schreiben können, sind asynchrone Softwarekonstrukte.

Versprechen vereinfachen die Arbeit mit asynchronem Code erheblich. Selbst wenn Sie das Konstrukt async / await bevorzugen , können Sie nicht auf das Verständnis der Versprechen verzichten , da dieses Konstrukt auf diesen basiert.

Wenn Sie jedoch Versprechen verwenden, müssen Sie Funktionen deklarieren, die nach Abschluss des asynchronen Codes oder nach Abschluss des asynchronen Aufrufs einer bestimmten API aufgerufen werden.

Dies ist ein idealer Ort, um Pfeilfunktionen zu verwenden, insbesondere wenn die resultierende Funktion einen bestimmten Zustand hat und sich auf etwas im Objekt bezieht. Zum Beispiel könnte es so aussehen:

 this.doSomethingAsync().then((result) => { this.storeResult(result); }); 

▍ Objekttransformation


Ein weiterer häufiger Anwendungsfall für Pfeilfunktionen ist das Einkapseln von Objekttransformationen.

In Vue.js gibt es beispielsweise ein allgemeines Muster für das direkte Einfügen von Vuex-Speicherfragmenten in eine Vue-Komponente mithilfe von mapState .

Diese Operation enthält Deklarationen einer Reihe von "Konvertern", die genau auswählen, was für eine bestimmte Komponente aus dem anfänglichen vollständigen Zustand benötigt wird.

Diese einfachen Transformationen sind der perfekte Ort, um Pfeilfunktionen zu verwenden. Zum Beispiel:

 export default { computed: {   ...mapState({     results: state => state.results,     users: state => state.users,   }); } } 

Situationen, in denen Pfeilfunktionen nicht verwendet werden sollten


▍ Objektmethoden


Es gibt eine Reihe von Situationen, in denen die Verwendung von Pfeilfunktionen keine gute Idee ist. Pfeilfunktionen helfen bei geringer Verwendung nicht nur Programmierern, sondern werden auch zu einer Quelle von Problemen.

Die erste solche Situation besteht darin, Pfeilfunktionen als Objektmethoden zu verwenden. Der Ausführungskontext und das this , die für traditionelle Funktionen spezifisch sind, sind hier wichtig.

Früher war es beliebt, eine Kombination aus Klasseneigenschaften und Pfeilfunktionen zu verwenden, um Methoden mit "automatischer Bindung" zu erstellen, dh solche, die von Ereignishandlern verwendet werden können, aber an die Klasse gebunden bleiben. Es sah ungefähr so ​​aus:

 class Counter { counter = 0; handleClick = () => {   this.counter++; } } 

Mit einer ähnlichen Konstruktion hatte diese Funktion Zugriff auf die Daten dieser Instanz, auch wenn die handleClick Funktion vom Ereignishandler aufgerufen wurde und nicht im Kontext einer Instanz der Counter Klasse.

Dieser Ansatz hat jedoch viele Nachteile, denen dieses Material gewidmet ist.

Obwohl die Verwendung einer Pfeilfunktion hier natürlich eine bequeme Möglichkeit zum Binden einer Funktion darstellt, ist das Verhalten dieser Funktion in vielerlei Hinsicht alles andere als intuitiv und beeinträchtigt das Testen und das Erstellen von Problemen in Situationen, in denen beispielsweise versucht wird, das entsprechende Objekt als Prototyp zu verwenden.

Verwenden Sie in solchen Fällen anstelle von Pfeilfunktionen normale Funktionen und binden Sie gegebenenfalls eine Instanz des Objekts im Konstruktor an diese:

 class Counter { counter = 0; handleClick() {   this.counter++; } constructor() {   this.handleClick = this.handleClick.bind(this); } } 

▍Lange Anrufketten


Pfeilfunktionen können zu Problemen führen, wenn sie in vielen verschiedenen Kombinationen verwendet werden sollen, insbesondere in langen Ketten von Funktionsaufrufen.

Der Hauptgrund für solche Probleme, wie bei der Verwendung anonymer Funktionen, ist, dass sie äußerst uninformative Ergebnisse der Aufrufstapelverfolgung liefern.

Dies ist nicht so schlimm, wenn es beispielsweise nur eine Verschachtelungsebene von Funktionsaufrufen gibt, beispielsweise wenn es sich um die im Iterator verwendete Funktion handelt. Wenn jedoch alle verwendeten Funktionen deklarierte Pfeilfunktionen sind und sich diese Funktionen gegenseitig aufrufen, ist es im Fehlerfall nicht einfach herauszufinden, was passiert. Die Fehlermeldungen sehen ungefähr so ​​aus:

 {anonymous}() {anonymous}() {anonymous}() {anonymous}() {anonymous}() 

▍ Funktionen mit dynamischem Kontext


Die letzte der Situationen, in denen Pfeilfunktionen zu Problemen führen können, besteht darin, sie dort zu verwenden, wo Sie eine dynamische Bindung benötigen.

Wenn in solchen Situationen Pfeilfunktionen verwendet werden, funktioniert die Dynamik this Bindung nicht. Diese unangenehme Überraschung kann dazu führen, dass man sich über die Gründe Gedanken macht, was mit denen passiert, die mit Code arbeiten müssen, in dem die Pfeilfunktionen falsch verwendet werden.

Hier sind einige Dinge zu beachten, wenn Sie die Verwendung von Pfeilfunktionen in Betracht ziehen:

  • Ereignishandler werden mit this Bindung an das Ereignisattribut currentTarget .
  • Wenn Sie noch jQuery verwenden, beachten Sie, dass die meisten jQuery-Methoden this an das ausgewählte DOM-Element binden.
  • Wenn Sie Vue.js verwenden, binden Methoden und berechnete Funktionen dies normalerweise an die Vue-Komponente.

Natürlich können Pfeilfunktionen absichtlich verwendet werden, um das Standardverhalten von Softwaremechanismen zu ändern. Insbesondere in Fällen mit jQuery und Vue steht dies jedoch häufig im Widerspruch zur normalen Funktionsweise des Systems, was dazu führt, dass der Programmierer nicht verstehen kann, warum ein völlig normaler Code plötzlich nicht mehr funktioniert.

Zusammenfassung


Pfeilfunktionen sind eine wunderbare neue JavaScript-Funktion. Sie ermöglichen in vielen Situationen das Schreiben von bequemerem Code als zuvor. Aber wie bei anderen Merkmalen haben sie sowohl Vor- als auch Nachteile. Daher müssen Sie Pfeilfunktionen verwenden, wenn sie nützlich sein können, ohne sie als vollständigen Ersatz für normale Funktionen zu betrachten.

Liebe Leser! Sind Sie auf Situationen gestoßen, in denen die Verwendung von Pfeilfunktionen zu Fehlern, Unannehmlichkeiten oder unerwartetem Verhalten von Programmen führt?

Source: https://habr.com/ru/post/de428566/


All Articles