Sind 'Pfeilfunktionen' und 'Funktionen' gleichwertig / austauschbar?

520

Pfeilfunktionen in ES2015 bieten eine präzisere Syntax.

  • Kann ich jetzt alle meine Funktionsdeklarationen / -ausdrücke durch Pfeilfunktionen ersetzen?
  • Worauf muss ich achten?

Beispiele:

Konstruktorfunktion

function User(name) {
  this.name = name;
}

// vs

const User = name => {
  this.name = name;
};

Prototypmethoden

User.prototype.getName = function() {
  return this.name;
};

// vs

User.prototype.getName = () => this.name;

Objektmethoden (Literalmethoden)

const obj = {
  getName: function() {
    // ...
  }
};

// vs

const obj = {
  getName: () => {
    // ...
  }
};

Rückrufe

setTimeout(function() {
  // ...
}, 500);

// vs

setTimeout(() => {
  // ...
}, 500);

Variadische Funktionen

function sum() {
  let args = [].slice.call(arguments);
  // ...
}

// vs
const sum = (...args) => {
  // ...
};
Felix Kling
quelle
5
Ähnliche Fragen zu Pfeilfunktionen tauchen immer häufiger auf, da ES2015 immer beliebter wird. Ich hatte nicht das Gefühl, dass es eine gute kanonische Frage / Antwort für dieses Problem gibt, also habe ich diese erstellt. Wenn Sie der Meinung sind, dass es bereits eine gute gibt, lassen Sie es mich bitte wissen und ich werde diese als Duplikat schließen oder löschen. Fühlen Sie sich frei, die Beispiele zu verbessern oder neue hinzuzufügen.
Felix Kling
2
Was ist mit JavaScript ecma6, das die normale Funktion in die Pfeilfunktion ändert ? Natürlich kann eine normale Frage niemals so gut und allgemein sein wie eine, die speziell als kanonisch geschrieben wurde.
Bergi
Schauen Sie sich dieses Plnkr-Beispiel an. Die Variable thisist timesCalledbei jedem Aufruf der Schaltfläche nur um 1 sehr unterschiedlich . Was meine persönliche Frage beantwortet: .click( () => { } )und .click(function() { }) beide erzeugen die gleiche Anzahl von Funktionen, wenn sie in einer Schleife verwendet werden, wie Sie aus der Guid-Anzahl im Plnkr sehen können.
Abbaf33f

Antworten:

750

tl; dr: Nein! Pfeilfunktionen und Funktionsdeklarationen / -ausdrücke sind nicht äquivalent und können nicht blind ersetzt werden.
Wenn die Funktion, die Sie ersetzen möchten, nicht verwendet thiswird argumentsund nicht mit aufgerufen wird new, dann ja.


Wie so oft: es kommt darauf an . Pfeilfunktionen haben ein anderes Verhalten als Funktionsdeklarationen / -ausdrücke. Schauen wir uns also zuerst die Unterschiede an:

1. Lexikalisch thisundarguments

Pfeilfunktionen haben keine eigene thisoder argumentsBindung. Stattdessen werden diese Bezeichner wie jede andere Variable im lexikalischen Bereich aufgelöst. Das bedeutet, dass innerhalb einer Pfeilfunktion thisund unter argumentsBezugnahme auf die Werte von thisund argumentsin der Umgebung die Pfeilfunktion definiert ist (dh "außerhalb" der Pfeilfunktion):

// Example using a function expression
function createObject() {
  console.log('Inside `createObject`:', this.foo);
  return {
    foo: 42,
    bar: function() {
      console.log('Inside `bar`:', this.foo);
    },
  };
}

createObject.call({foo: 21}).bar(); // override `this` inside createObject

// Example using a arrow function
function createObject() {
  console.log('Inside `createObject`:', this.foo);
  return {
    foo: 42,
    bar: () => console.log('Inside `bar`:', this.foo),
  };
}

createObject.call({foo: 21}).bar(); // override `this` inside createObject

thisBezieht sich im Fall des Funktionsausdrucks auf das Objekt, das innerhalb des Objekts erstellt wurde createObject. In Pfeil Funktion Fall thisbezieht sich auf thisvon createObjectselbst.

Dies macht Pfeilfunktionen nützlich, wenn Sie auf thisdie aktuelle Umgebung zugreifen müssen :

// currently common pattern
var that = this;
getData(function(data) {
  that.data = data;
});

// better alternative with arrow functions
getData(data => {
  this.data = data;
});

Beachten Sie, dass dies auch bedeutet, dass es nicht möglich ist, eine Pfeilfunktion thismit .bindoder festzulegen .call.

Wenn Sie nicht sehr vertraut sind this, lesen Sie

2. Pfeilfunktionen können nicht mit aufgerufen werden new

ES2015 unterscheidet zwischen Funktionen, die aufrufbar sind , und Funktionen, die konstruierbar sind . Wenn eine Funktion konstruierbar ist, kann sie mit aufgerufen werden new, dh new User(). Wenn eine Funktion aufrufbar ist, kann sie ohne aufgerufen werden new(dh normaler Funktionsaufruf).

Durch Funktionsdeklarationen / -ausdrücke erstellte Funktionen sind sowohl konstruierbar als auch aufrufbar.
Pfeilfunktionen (und Methoden) können nur aufgerufen werden. classKonstruktoren sind nur konstruierbar.

Wenn Sie versuchen, eine nicht aufrufbare Funktion aufzurufen oder eine nicht aufbaubare Funktion zu erstellen, wird ein Laufzeitfehler angezeigt.


In diesem Wissen können wir Folgendes feststellen.

Austauschbar:

  • Funktionen, die thisoder nicht verwenden arguments.
  • Funktionen, die mit verwendet werden .bind(this)

Nicht austauschbar:

  • Konstruktorfunktionen
  • Funktion / Methoden, die einem Prototyp hinzugefügt wurden (weil sie normalerweise verwendet werden this)
  • Variadische Funktionen (falls verwendet arguments(siehe unten))

Schauen wir uns das anhand Ihrer Beispiele genauer an:

Konstruktorfunktion

Dies funktioniert nicht, da Pfeilfunktionen nicht mit aufgerufen werden können new. Verwenden Sie weiterhin eine Funktionsdeklaration / einen Funktionsausdruck oder verwenden Sie class.

Prototypmethoden

Höchstwahrscheinlich nicht, da Prototypmethoden normalerweise thisfür den Zugriff auf die Instanz verwendet werden. Wenn sie nicht verwenden this, können Sie es ersetzen. Wenn Sie sich jedoch hauptsächlich für eine prägnante Syntax interessieren, verwenden Sie diese classmit ihrer prägnanten Methodensyntax:

class User {
  constructor(name) {
    this.name = name;
  }

  getName() {
    return this.name;
  }
}

Objektmethoden

Ähnliches gilt für Methoden in einem Objektliteral. Wenn die Methode auf das Objekt selbst verweisen möchte this, verwenden Sie weiterhin Funktionsausdrücke oder verwenden Sie die neue Methodensyntax:

const obj = {
  getName() {
    // ...
  },
};

Rückrufe

Es hängt davon ab, ob. Sie sollten es auf jeden Fall ersetzen, wenn Sie das äußere Aliasing verwenden thisoder Folgendes verwenden .bind(this):

// old
setTimeout(function() {
  // ...
}.bind(this), 500);

// new
setTimeout(() => {
  // ...
}, 500);

Aber: Wenn der Code, der den Rückruf aufruft, explizit thisauf einen bestimmten Wert festgelegt wird, wie dies häufig bei Ereignishandlern, insbesondere bei jQuery, der Fall ist und der Rückruf this(oder arguments) verwendet, können Sie keine Pfeilfunktion verwenden !

Variadische Funktionen

Da Pfeilfunktionen keine eigenen haben arguments, können Sie sie nicht einfach durch eine Pfeilfunktion ersetzen. ES2015 bietet jedoch eine Alternative zur Verwendung arguments: den Parameter rest .

// old
function sum() {
  let args = [].slice.call(arguments);
  // ...
}

// new
const sum = (...args) => {
  // ...
};

Verwandte Frage:

Weitere Ressourcen:

Felix Kling
quelle
6
Möglicherweise erwähnenswert, dass das Lexikon thisauch Auswirkungen hat superund dass sie keine haben .prototype.
Loganfsmyth
1
Es wäre auch gut zu erwähnen, dass sie nicht syntaktisch austauschbar sind - eine Pfeilfunktion ( AssignmentExpression) kann nicht einfach überall dort abgelegt werden, wo es ein Funktionsausdruck ( PrimaryExpression) kann, und sie stolpert ziemlich häufig (insbesondere seit dem Parsen) Fehler in wichtigen JS-Implementierungen).
JMM
@JMM: "Es stolpert ziemlich häufig" können Sie ein konkretes Beispiel geben? Beim Überfliegen der Spezifikation scheint es, dass die Stellen, an denen Sie eine FE, aber keine AF einfügen können, ohnehin zu Laufzeitfehlern führen würden ...
Felix Kling
Klar, ich meine Dinge wie den Versuch, eine Pfeilfunktion wie einen Funktionsausdruck ( () => {}()) sofort aufzurufen oder so etwas zu tun x || () => {}. Das meine ich: Laufzeitfehler (Analysefehler). (Und obwohl dies der Fall ist, denken die Leute ziemlich häufig, dass der Fehler fehlerhaft ist.) Versuchen Sie nur, Logikfehler abzudecken, die unbemerkt bleiben würden, weil sie beim Analysieren oder Ausführen nicht unbedingt fehlerhaft sind? newEin Fehler ist ein Laufzeitfehler, oder?
JMM
Hier sind einige Links, die in freier Wildbahn auftauchen : Substack / Node-Browserify # 1499 , Babel / Babel-Eslint # 245 (dies ist ein asynchroner Pfeil, aber ich denke, es ist das gleiche Grundproblem) und eine Reihe von Problemen Babel, die jetzt schwer zu finden sind, aber hier ist ein T2847 .
JMM
11

Pfeilfunktionen => bisher beste ES6-Funktion. Sie sind eine enorm leistungsstarke Ergänzung zu ES6, die ich ständig benutze.

Warten Sie, Sie können die Pfeilfunktion nicht überall in Ihrem Code verwenden. Sie funktioniert nicht in allen Fällen, in thisdenen Pfeilfunktionen nicht verwendet werden können. Ohne Zweifel ist die Pfeilfunktion eine großartige Ergänzung, die Code-Einfachheit bringt.

Sie können jedoch keine Pfeilfunktion verwenden, wenn ein dynamischer Kontext erforderlich ist: Definieren von Methoden, Erstellen von Objekten mit Konstruktoren, Abrufen des Ziels beim Behandeln von Ereignissen.

Pfeilfunktionen sollten NICHT verwendet werden, weil:

  1. Sie haben nicht this

    Es verwendet "lexikalisches Scoping", um herauszufinden, was der Wert von " this" sein sollte. Im einfachen Wort lexikalisches Scoping wird " this" aus dem Inneren des Funktionskörpers verwendet.

  2. Sie haben nicht arguments

    Pfeilfunktionen haben kein argumentsObjekt. Die gleiche Funktionalität kann jedoch mit Ruheparametern erreicht werden.

    let sum = (...args) => args.reduce((x, y) => x + y, 0) sum(3, 3, 1) // output - 7 `

  3. Sie können nicht mit verwendet werden new

    Pfeilfunktionen können keine Construtoren sein, da sie keine Prototyp-Eigenschaft haben.

Wann und wann nicht?

  1. Verwenden Sie diese Option nicht, um eine Funktion als Eigenschaft im Objektliteral hinzuzufügen, da wir nicht darauf zugreifen können.
  2. Funktionsausdrücke eignen sich am besten für Objektmethoden. Pfeil Funktionen sind am besten für Rückrufe oder Methoden wie map, reduceoder forEach.
  3. Verwenden Sie Funktionsdeklarationen für Funktionen, die Sie beim Namen aufrufen möchten (weil sie hochgezogen sind).
  4. Verwenden Sie Pfeilfunktionen für Rückrufe (da diese tendenziell kürzer sind).
Ashutosh
quelle
2
die 2. Sie haben keine Argumente, es tut mir leid, ist nicht wahr, man kann Argumente haben, ohne den Operator ... zu verwenden, vielleicht möchten Sie sagen, dass Sie kein Array als Argument haben
Carmine Tambascia
@CarmineTambascia Lesen Sie hier mehr über das spezielle argumentsObjekt, das in den
Pfeilfunktionen
0

Um Pfeilfunktionen mit zu verwenden function.prototype.call, habe ich eine Hilfsfunktion für den Objektprototyp erstellt:

  // Using
  // @func = function() {use this here} or This => {use This here}
  using(func) {
    return func.call(this, this);
  }

Verwendungszweck

  var obj = {f:3, a:2}
  .using(This => This.f + This.a) // 5

Bearbeiten

Du brauchst keinen Helfer. Du könntest es tun:

var obj = {f:3, a:2}
(This => This.f + This.a).call(undefined, obj); // 5
toddmo
quelle