Dem Vorschlag zufolge zielten Pfeile darauf ab, "mehrere häufig auftretende Probleme zu lösen und zu lösen Function Expression
". Sie wollten die Sache verbessern, indem sie this
lexikalisch binden und eine knappe Syntax bieten.
Jedoch,
- Man kann nicht konsequent
this
lexikalisch binden
- Die Syntax der Pfeilfunktion ist heikel und mehrdeutig
Daher können Pfeilfunktionen zu Verwirrung und Fehlern führen und sollten aus dem Vokabular eines JavaScript-Programmierers ausgeschlossen und durch function
ausschließlich ersetzt werden.
In Bezug auf lexikalische this
this
ist problematisch:
function Book(settings) {
this.settings = settings;
this.pages = this.createPages();
}
Book.prototype.render = function () {
this.pages.forEach(function (page) {
page.draw(this.settings);
}, this);
};
Mit den Pfeilfunktionen soll das Problem behoben werden, bei dem auf eine Eigenschaft this
innerhalb eines Rückrufs zugegriffen werden muss. Dazu gibt es bereits mehrere Möglichkeiten: Sie können this
einer Variablen zuweisen , bind
das dritte Argument verwenden oder verwenden, das für die Array
Aggregatmethoden verfügbar ist . Pfeile scheinen jedoch die einfachste Problemumgehung zu sein, sodass die Methode folgendermaßen überarbeitet werden könnte:
this.pages.forEach(page => page.draw(this.settings));
Überlegen Sie jedoch, ob der Code eine Bibliothek wie jQuery verwendet, deren Methoden this
speziell gebunden sind . Nun gibt es zwei this
Werte, mit denen man sich befassen muss:
Book.prototype.render = function () {
var book = this;
this.$pages.each(function (index) {
var $page = $(this);
book.draw(book.currentPage + index, $page);
});
};
Wir müssen verwenden , function
um each
zu binden this
dynamisch. Wir können hier keine Pfeilfunktion verwenden.
Der Umgang mit mehreren this
Werten kann auch verwirrend sein, da es schwierig ist zu wissen, über was this
ein Autor gesprochen hat:
function Reader() {
this.book.on('change', function () {
this.reformat();
});
}
Wollte der Autor tatsächlich anrufen Book.prototype.reformat
? Oder hat er vergessen zu binden this
und beabsichtigt anzurufen Reader.prototype.reformat
? Wenn wir den Handler in eine Pfeilfunktion ändern, werden wir uns ebenfalls fragen, ob der Autor die Dynamik wollte this
, aber einen Pfeil gewählt hat, weil er in eine Zeile passt:
function Reader() {
this.book.on('change', () => this.reformat());
}
Man könnte sagen: "Ist es außergewöhnlich, dass Pfeile manchmal die falsche Funktion sind? Wenn wir nur selten dynamische this
Werte benötigen , ist es immer noch in Ordnung, die meiste Zeit Pfeile zu verwenden."
Aber fragen Sie sich Folgendes: "Wäre es 'wert', Code zu debuggen und festzustellen, dass das Ergebnis eines Fehlers durch einen 'Randfall' verursacht wurde?" Ich würde es vorziehen, Probleme nicht nur die meiste Zeit zu vermeiden, sondern 100% der Zeit.
Es gibt einen besseren Weg: Immer verwenden function
( this
kann also immer dynamisch gebunden werden) und immer this
über eine Variable referenzieren . Variablen sind lexikalisch und nehmen viele Namen an. Das Zuweisen this
zu einer Variablen macht Ihre Absichten klar:
function Reader() {
var reader = this;
reader.book.on('change', function () {
var book = this;
book.reformat();
reader.reformat();
});
}
Darüber hinaus wird durch die ständige Zuweisung this
zu einer Variablen (auch wenn eine einzelne this
oder keine andere Funktion vorhanden ist) sichergestellt, dass die eigenen Absichten auch nach einer Änderung des Codes klar bleiben.
Auch Dynamik this
ist kaum außergewöhnlich. jQuery wird auf über 50 Millionen Websites verwendet (Stand: Februar 2016). Hier sind andere APIs, die this
dynamisch binden :
- Mocha (~ 120k Downloads gestern) stellt Methoden für seine Tests über zur Verfügung
this
.
- Grunt (~ 63k Downloads gestern) macht Methoden für Build-Aufgaben über verfügbar
this
.
- Backbone (~ 22.000 Downloads gestern) definiert Methoden für den Zugriff
this
.
- Ereignis-APIs (wie die DOMs) beziehen sich auf ein
EventTarget
with this
.
- Prototyp-APIs, die gepatcht oder erweitert werden, beziehen sich auf Instanzen mit
this
.
(Statistiken über http://trends.builtwith.com/javascript/jQuery und https://www.npmjs.com .)
Sie benötigen wahrscheinlich this
bereits dynamische Bindungen.
Ein Lexikon this
wird manchmal erwartet, aber manchmal nicht; so wie eine Dynamik this
manchmal erwartet wird, aber manchmal nicht. Zum Glück gibt es einen besseren Weg, der immer die erwartete Bindung erzeugt und kommuniziert.
In Bezug auf die knappe Syntax
Pfeilfunktionen haben es geschafft, eine "kürzere syntaktische Form" für Funktionen bereitzustellen. Aber werden diese kürzeren Funktionen Sie erfolgreicher machen?
Ist x => x * x
"leichter zu lesen" als function (x) { return x * x; }
? Vielleicht liegt es daran, dass es wahrscheinlicher ist, dass eine einzelne, kurze Codezeile erzeugt wird. Nach Dysons Einfluss von Lesegeschwindigkeit und Zeilenlänge auf die Effektivität des Lesens vom Bildschirm ,
Eine mittlere Zeilenlänge (55 Zeichen pro Zeile) scheint ein effektives Lesen bei normaler und hoher Geschwindigkeit zu unterstützen. Dies führte zu einem Höchstmaß an Verständnis. . .
Ähnliche Begründungen werden für den bedingten (ternären) Operator und für einzeilige if
Anweisungen gemacht.
Schreiben Sie jedoch wirklich die einfachen mathematischen Funktionen, die im Vorschlag angegeben sind ? Meine Domänen sind nicht mathematisch, daher sind meine Unterprogramme selten so elegant. Vielmehr sehe ich häufig, dass Pfeilfunktionen eine Spaltenbegrenzung überschreiten und aufgrund des Editors oder Styleguides in eine andere Zeile umbrechen, wodurch die "Lesbarkeit" nach Dysons Definition ungültig wird.
Man könnte sich fragen: "Wie wäre es, wenn Sie nur die Kurzversion für kurze Funktionen verwenden, wenn möglich?" Aber jetzt widerspricht eine Stilregel einer Sprachbeschränkung: "Versuchen Sie, die kürzestmögliche Funktionsnotation zu verwenden, wobei zu beachten ist, dass manchmal nur die längste Notation this
wie erwartet gebunden wird ." Eine solche Verschmelzung macht Pfeile besonders anfällig für Missbrauch.
Es gibt zahlreiche Probleme mit der Pfeilfunktionssyntax:
const a = x =>
doSomething(x);
const b = x =>
doSomething(x);
doSomethingElse(x);
Beide Funktionen sind syntaktisch gültig. Aber es doSomethingElse(x);
ist nicht im Körper von b
, es ist nur eine schlecht eingerückte Aussage auf höchster Ebene.
Beim Erweitern auf die Blockform gibt es kein Implizit mehr return
, dessen Wiederherstellung man vergessen könnte. Aber der Ausdruck sollte möglicherweise nur eine Nebenwirkung hervorrufen. Wer weiß also, ob in return
Zukunft ein expliziter Ausdruck erforderlich sein wird?
const create = () => User.create();
const create = () => {
let user;
User.create().then(result => {
user = result;
return sendEmail();
}).then(() => user);
};
const create = () => {
let user;
return User.create().then(result => {
user = result;
return sendEmail();
}).then(() => user);
};
Was als Ruheparameter gedacht sein kann, kann als Spread-Operator analysiert werden:
processData(data, ...results => {}) // Spread
processData(data, (...results) => {}) // Rest
Die Zuweisung kann mit Standardargumenten verwechselt werden:
const a = 1;
let x;
const b = x => {}; // No default
const b = x = a => {}; // "Adding a default" instead creates a double assignment
const b = (x = a) => {}; // Remember to add parens
Blöcke sehen aus wie Objekte:
(id) => id // Returns `id`
(id) => {name: id} // Returns `undefined` (it's a labeled statement)
(id) => ({name: id}) // Returns an object
Was bedeutet das?
() => {}
Wollte der Autor ein No-Op oder eine Funktion erstellen, die ein leeres Objekt zurückgibt? ( Sollten wir in diesem Sinne jemals {
danach platzieren =>
? Sollten wir uns nur auf die Ausdruckssyntax beschränken? Dies würde die Häufigkeit der Pfeile weiter verringern.)
=>
sieht aus wie <=
und >=
:
x => 1 ? 2 : 3
x <= 1 ? 2 : 3
if (x => 1) {}
if (x >= 1) {}
Um einen Pfeilfunktionsausdruck sofort aufzurufen, muss er ()
außen platziert ()
werden. Die Platzierung innen ist jedoch gültig und kann beabsichtigt sein.
(() => doSomething()()) // Creates function calling value of `doSomething()`
(() => doSomething())() // Calls the arrow function
Wenn man jedoch (() => doSomething()());
mit der Absicht schreibt, einen sofort aufgerufenen Funktionsausdruck zu schreiben, passiert einfach nichts.
Es ist schwer zu argumentieren, dass Pfeilfunktionen in Anbetracht aller oben genannten Fälle "verständlicher" sind. Man könnte alle speziellen Regeln lernen, die erforderlich sind, um diese Syntax zu verwenden. Lohnt es sich wirklich?
Die Syntax von function
ist ausnahmsweise verallgemeinert. Zur Nutzung function
bedeutet , ausschließlich die Sprache selbst eine vom Schreiben verwirrend Code verhindert. Um Prozeduren zu schreiben, die in allen Fällen syntaktisch verstanden werden sollten, wähle ich function
.
In Bezug auf eine Richtlinie
Sie fordern eine Richtlinie an, die "klar" und "konsistent" sein muss. Die Verwendung von Pfeilfunktionen führt schließlich zu syntaktisch gültigem, logisch ungültigem Code, wobei beide Funktionsformen sinnvoll und willkürlich miteinander verflochten sind. Deshalb biete ich folgendes an:
Richtlinie für die Funktionsnotation in ES6:
- Erstellen Sie immer Prozeduren mit
function
.
- Immer
this
einer Variablen zuweisen . Nicht verwenden () => {}
.
Fixed this bound to scope at initialisation
als Einschränkung?