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 functionesclusivamente.
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à thisall'interno di un callback. Esistono già diversi modi per farlo: si potrebbe assegnare thisa una variabile, usare bindo usare il terzo argomento disponibile sui Arraymetodi 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 thisspeciale. Ora, ci sono due thisvalori 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 functionper eachlegarethis dinamicamente. Non possiamo usare una funzione freccia qui.
Trattare con più thisvalori 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 thise 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 thisvalori 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 thispuò sempre essere associato dinamicamente) e fai sempre riferimento thistramite una variabile. Le variabili sono lessicali e assumono molti nomi. L'assegnazione thisa 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 thiso nessuna altra funzione) garantisce che le intenzioni rimangano chiare anche dopo la modifica del codice.
Inoltre, la dinamica thisnon è 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 thisdinamicamente:
- 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
EventTargetcon 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 thisavrai già bisogno di associazioni dinamiche .
A thisvolte è previsto un lessico , ma a volte no; proprio come a thisvolte 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 ifistruzioni 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 returnsarà 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 functionesclusivamente 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
thisa una variabile. Non usare () => {}.
Fixed this bound to scope at initialisationuna limitazione?