Secondo la proposta , le frecce miravano a "affrontare e risolvere diversi punti dolenti comuni del tradizionale Function Expression
". Intendevano migliorare le cose vincolandothis
lessicamente e offrendo sintassi concisa.
Però,
- Non si può legare in modo coerente
this
lessicale
- La sintassi della funzione freccia è delicata e ambigua
Pertanto, le funzioni freccia creano opportunità di confusione ed errori e dovrebbero essere escluse dal vocabolario di un programmatore JavaScript, sostituito function
esclusivamente.
Per quanto riguarda lessicale this
this
è problematico:
function Book(settings) {
this.settings = settings;
this.pages = this.createPages();
}
Book.prototype.render = function () {
this.pages.forEach(function (page) {
page.draw(this.settings);
}, this);
};
Le funzioni freccia intendono risolvere il problema in cui è necessario accedere a una proprietà this
all'interno di un callback. Esistono già diversi modi per farlo: si potrebbe assegnare this
a una variabile, usare bind
o usare il terzo argomento disponibile sui Array
metodi aggregati. Eppure le frecce sembrano essere la soluzione più semplice, quindi il metodo potrebbe essere riformulato in questo modo:
this.pages.forEach(page => page.draw(this.settings));
Tuttavia, considera se il codice utilizzava una libreria come jQuery, i cui metodi si legano in modo this
speciale. Ora, ci sono due this
valori da affrontare:
Book.prototype.render = function () {
var book = this;
this.$pages.each(function (index) {
var $page = $(this);
book.draw(book.currentPage + index, $page);
});
};
Dobbiamo usare function
per each
legarethis
dinamicamente. Non possiamo usare una funzione freccia qui.
Trattare con più this
valori può anche essere fonte di confusione, perché è difficile sapere qualethis
autore stesse parlando:
function Reader() {
this.book.on('change', function () {
this.reformat();
});
}
L'autore intendeva davvero chiamare Book.prototype.reformat
? O ha dimenticato di legare this
e ha intenzione di chiamare Reader.prototype.reformat
? Se cambiamo il gestore in una funzione freccia, ci chiederemo allo stesso modo se l'autore volesse la dinamicathis
, eppure abbia scelto una freccia perché si adattava su una riga:
function Reader() {
this.book.on('change', () => this.reformat());
}
Ci si potrebbe porre: "È eccezionale che le frecce a volte possano essere la funzione sbagliata da usare? Forse se avessimo bisogno solo raramente di this
valori dinamici , sarebbe comunque bene usare le frecce per la maggior parte del tempo."
Ma chiediti questo: "Ne varrebbe la pena eseguire il debug del codice e scoprire che il risultato di un errore è stato causato da un" caso limite "?" Preferirei evitare problemi non solo la maggior parte delle volte, ma Il 100% delle volte.
C'è un modo migliore: usa sempre function
(quindi this
può sempre essere associato dinamicamente) e fai sempre riferimento this
tramite una variabile. Le variabili sono lessicali e assumono molti nomi. L'assegnazione this
a una variabile renderà chiare le tue intenzioni:
function Reader() {
var reader = this;
reader.book.on('change', function () {
var book = this;
book.reformat();
reader.reformat();
});
}
Inoltre, l' assegnazione semprethis
a una variabile (anche quando è presente una singola this
o nessuna altra funzione) garantisce che le intenzioni rimangano chiare anche dopo la modifica del codice.
Inoltre, la dinamica this
non è certo eccezionale. jQuery è utilizzato su oltre 50 milioni di siti Web (al momento della stesura di questo documento nel febbraio 2016). Ecco altre API che si legano this
dinamicamente:
- Mocha (~ 120k download ieri) espone metodi per i suoi test tramite
this
.
- Grunt (~ 63k download ieri) espone metodi per compilare attività tramite
this
.
- Backbone (~ 22k download ieri) definisce i metodi di accesso
this
.
- Le API degli eventi (come i DOM) si riferiscono a
EventTarget
con this
.
- Le API prototipo che sono patchate o estese si riferiscono a istanze con
this
.
(Statistiche tramite http://trends.builtwith.com/javascript/jQuery e https://www.npmjs.com .)
Probabilmente this
avrai già bisogno di associazioni dinamiche .
A this
volte è previsto un lessico , ma a volte no; proprio come a this
volte ci si aspetta una dinamica , ma a volte no. Per fortuna, esiste un modo migliore, che produce e comunica sempre l'associazione prevista.
Per quanto riguarda la sintassi concisa
Le funzioni freccia sono riuscite a fornire una "forma sintattica più corta" per le funzioni. Ma queste funzioni più brevi ti renderanno più efficace?
x => x * x
" È più facile da leggere" di function (x) { return x * x; }
? Forse lo è, perché è più probabile che produca un'unica riga di codice breve. Rispetto a Dyson's L'influenza della velocità di lettura e della lunghezza della linea sull'efficacia della lettura dallo schermo ,
Una lunghezza di riga media (55 caratteri per riga) sembra supportare una lettura efficace a velocità normale e veloce. Ciò ha prodotto il massimo livello di comprensione. . .
Giustificazioni simili vengono fatte per l'operatore condizionale (ternario) e per le if
istruzioni a riga singola .
Tuttavia, stai davvero scrivendo le semplici funzioni matematiche pubblicizzate nella proposta ? I miei domini non sono matematici, quindi le mie subroutine raramente sono così eleganti. Piuttosto, comunemente vedo che le funzioni delle frecce infrangono un limite di colonna e si spostano su un'altra riga a causa dell'editor o della guida di stile, che annulla la "leggibilità" secondo la definizione di Dyson.
Si potrebbe pensare: "Che ne dici di usare la versione breve per funzioni brevi, quando possibile?" Ma ora una regola stilistica contraddice un vincolo linguistico: "Cerca di usare la notazione di funzione più breve possibile, tenendo presente che a volte si legherà solo la notazione più lungathis
come previsto". Tale conflazione rende le frecce particolarmente soggette ad uso improprio.
Esistono numerosi problemi con la sintassi della funzione freccia:
const a = x =>
doSomething(x);
const b = x =>
doSomething(x);
doSomethingElse(x);
Entrambe queste funzioni sono sintatticamente valide. Ma doSomethingElse(x);
non è nel corpo di b
, è solo un'affermazione di alto livello scarsamente indentata.
Quando si espande nella forma di blocco, non c'è più un implicito return
, che si potrebbe dimenticare di ripristinare. Ma l'espressione può solo avere lo scopo di produrre un effetto collaterale, quindi chissà se un esplicito return
sarà necessario andare avanti?
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);
};
Ciò che può essere inteso come parametro di riposo può essere analizzato come operatore di diffusione:
processData(data, ...results => {}) // Spread
processData(data, (...results) => {}) // Rest
L'assegnazione può essere confusa con argomenti predefiniti:
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
I blocchi sembrano oggetti:
(id) => id // Returns `id`
(id) => {name: id} // Returns `undefined` (it's a labeled statement)
(id) => ({name: id}) // Returns an object
Cosa significa questo?
() => {}
L'autore intendeva creare una no-op o una funzione che restituisce un oggetto vuoto? (Con questo in mente, dovremmo mai posizionarci {
dopo =>
? Dovremmo limitarci solo alla sintassi dell'espressione? Ciò ridurrebbe ulteriormente la frequenza delle frecce.)
=>
sembra <=
e >=
:
x => 1 ? 2 : 3
x <= 1 ? 2 : 3
if (x => 1) {}
if (x >= 1) {}
Per invocare immediatamente un'espressione della funzione freccia, è necessario posizionarla ()
all'esterno, ma la collocazione ()
all'interno è valida e potrebbe essere intenzionale.
(() => doSomething()()) // Creates function calling value of `doSomething()`
(() => doSomething())() // Calls the arrow function
Sebbene, se si scrive (() => doSomething()());
con l'intenzione di scrivere un'espressione di funzione immediatamente invocata, semplicemente non accadrà nulla.
È difficile sostenere che le funzioni delle frecce siano "più comprensibili" tenendo conto di tutti i casi di cui sopra. Si potrebbero imparare tutte le regole speciali richieste per utilizzare questa sintassi. Ne vale davvero la pena?
La sintassi di function
è generalizzata in modo ineccepibile. Usare function
esclusivamente significa che il linguaggio stesso impedisce di scrivere codice confuso. Per scrivere procedure che dovrebbero essere sintatticamente comprese in tutti i casi, scelgo function
.
Per quanto riguarda una linea guida
Si richiede una linea guida che deve essere "chiara" e "coerente". L'uso delle funzioni freccia alla fine porterà a un codice sintatticamente valido, logicamente non valido, con entrambe le forme di funzione intrecciate, in modo significativo e arbitrario. Pertanto, offro quanto segue:
Linee guida per la notazione delle funzioni in ES6:
- Creare sempre procedure con
function
.
- Assegna sempre
this
a una variabile. Non usare () => {}
.
Fixed this bound to scope at initialisation
una limitazione?