Selon la proposition , les flèches visaient "à aborder et à résoudre plusieurs points communs douloureux du traditionnel Function Expression
". Ils avaient l'intention d'améliorer les choses en liant this
lexicalement et en offrant une syntaxe concise.
cependant,
- On ne peut pas toujours se lier
this
lexicalement
- La syntaxe de la fonction flèche est délicate et ambiguë
Par conséquent, les fonctions fléchées créent des opportunités de confusion et d'erreurs et doivent être exclues du vocabulaire d'un programmeur JavaScript, remplacées par function
exclusivement.
Concernant lexical this
this
est problématique:
function Book(settings) {
this.settings = settings;
this.pages = this.createPages();
}
Book.prototype.render = function () {
this.pages.forEach(function (page) {
page.draw(this.settings);
}, this);
};
Les fonctions fléchées visent à résoudre le problème où nous devons accéder à une propriété de l' this
intérieur d'un rappel. Il y a déjà plusieurs façons de le faire: On pourrait assigner this
à une variable, utiliser bind
ou utiliser le 3e argument disponible sur les Array
méthodes d'agrégation. Pourtant, les flèches semblent être la solution de contournement la plus simple, donc la méthode pourrait être refactorisée comme ceci:
this.pages.forEach(page => page.draw(this.settings));
Cependant, considérez si le code a utilisé une bibliothèque comme jQuery, dont les méthodes se lient this
spécialement. Maintenant, il y a deux this
valeurs à gérer:
Book.prototype.render = function () {
var book = this;
this.$pages.each(function (index) {
var $page = $(this);
book.draw(book.currentPage + index, $page);
});
};
Nous devons utiliser function
afin each
de lier this
dynamiquement. Nous ne pouvons pas utiliser de fonction de flèche ici.
Gérer plusieurs this
valeurs peut également être source de confusion, car il est difficile de savoir de quel this
auteur parlait:
function Reader() {
this.book.on('change', function () {
this.reformat();
});
}
L'auteur avait-il réellement l'intention d'appeler Book.prototype.reformat
? Ou a-t-il oublié de se lier this
et avait-il l'intention d'appeler Reader.prototype.reformat
? Si nous changeons le gestionnaire en une fonction de flèche, nous nous demanderons également si l'auteur voulait la dynamique this
, mais a choisi une flèche car elle tient sur une seule ligne:
function Reader() {
this.book.on('change', () => this.reformat());
}
On peut se poser la question suivante: "Est-il exceptionnel que les flèches soient parfois la mauvaise fonction à utiliser? Peut-être que si nous n'avons que rarement besoin de this
valeurs dynamiques , il serait toujours acceptable d'utiliser des flèches la plupart du temps."
Mais demandez-vous ceci: «Serait-il« utile »de déboguer du code et de découvrir que le résultat d'une erreur a été provoqué par un« cas de bord »?» «Je préfère éviter les problèmes non seulement la plupart du temps, mais 100% du temps.
Il y a une meilleure façon: toujours utiliser function
(donc this
peut toujours être lié dynamiquement), et toujours référencer this
via une variable. Les variables sont lexicales et prennent plusieurs noms. L'affectation this
à une variable rendra vos intentions claires:
function Reader() {
var reader = this;
reader.book.on('change', function () {
var book = this;
book.reformat();
reader.reformat();
});
}
De plus, toujours assigner this
à une variable (même quand il y a une seule this
ou aucune autre fonction) garantit que ses intentions restent claires même après que le code est changé.
De plus, la dynamique this
n'est guère exceptionnelle. jQuery est utilisé sur plus de 50 millions de sites Web (au moment de la rédaction de ce rapport en février 2016). Voici d'autres API liant this
dynamiquement:
- Mocha (~ 120k téléchargements hier) expose les méthodes de ses tests via
this
.
- Grunt (~ 63 000 téléchargements hier) expose les méthodes de création de tâches via
this
.
- Backbone (~ 22k téléchargements hier) définit les méthodes d'accès
this
.
- Les API d'événement (comme les DOM) font référence à un
EventTarget
avec this
.
- Les API prototypées corrigées ou étendues font référence aux instances avec
this
.
(Statistiques via http://trends.builtwith.com/javascript/jQuery et https://www.npmjs.com .)
Il est probable que vous this
ayez déjà besoin de liaisons dynamiques .
Un lexical this
est parfois attendu, mais parfois non; tout comme une dynamique this
est parfois attendue, mais parfois non. Heureusement, il existe un meilleur moyen, qui produit et communique toujours la liaison attendue.
Concernant la syntaxe laconique
Les fonctions fléchées ont réussi à fournir une "forme syntaxique plus courte" pour les fonctions. Mais ces fonctions plus courtes vous rendront-elles plus efficaces?
Est x => x * x
"plus facile à lire" que function (x) { return x * x; }
? C'est peut-être le cas, car il est plus susceptible de produire une seule ligne de code courte. Accor à Dyson L'influence de la vitesse de lecture et de la longueur de ligne sur l'efficacité de la lecture à l'écran ,
Une longueur de ligne moyenne (55 caractères par ligne) semble prendre en charge une lecture efficace à des vitesses normales et rapides. Cela a produit le plus haut niveau de compréhension. . .
Des justifications similaires sont faites pour l'opérateur conditionnel (ternaire) et pour les if
instructions sur une seule ligne .
Cependant, écrivez- vous vraiment les fonctions mathématiques simples annoncées dans la proposition ? Mes domaines ne sont pas mathématiques, donc mes sous-programmes sont rarement aussi élégants. Au contraire, je vois souvent que les fonctions fléchées dépassent une limite de colonne et se terminent par une autre ligne en raison de l'éditeur ou du guide de style, ce qui annule la «lisibilité» selon la définition de Dyson.
On pourrait se poser la question: «Que diriez-vous d'utiliser la version courte pour les fonctions courtes, lorsque cela est possible? Mais maintenant, une règle stylistique contredit une contrainte de langage: "Essayez d'utiliser la notation de fonction la plus courte possible, en gardant à l'esprit que parfois seule la notation la plus longue se liera this
comme prévu." Cette confusion rend les flèches particulièrement susceptibles d'être utilisées à mauvais escient.
Il existe de nombreux problèmes avec la syntaxe de la fonction flèche:
const a = x =>
doSomething(x);
const b = x =>
doSomething(x);
doSomethingElse(x);
Ces deux fonctions sont syntaxiquement valides. Mais ce doSomethingElse(x);
n'est pas dans le corps de b
, c'est juste une déclaration de haut niveau mal indentée.
Lors de l'expansion vers la forme bloc, il n'y a plus d'implicite return
, que l'on pourrait oublier de restaurer. Mais l'expression n'a peut- être été conçue que pour produire un effet secondaire, alors qui sait si une explicite return
sera nécessaire à l'avenir?
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);
};
Ce qui peut être prévu comme paramètre de repos peut être analysé en tant qu'opérateur d'étalement:
processData(data, ...results => {}) // Spread
processData(data, (...results) => {}) // Rest
L'affectation peut être confondue avec les arguments par défaut:
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
Les blocs ressemblent à des objets:
(id) => id // Returns `id`
(id) => {name: id} // Returns `undefined` (it's a labeled statement)
(id) => ({name: id}) // Returns an object
Qu'est-ce que ça veut dire?
() => {}
L'auteur avait-il l'intention de créer un no-op, ou une fonction qui renvoie un objet vide? (Dans cet esprit, devrions-nous jamais placer {
après =>
? Devrions-nous nous limiter à la syntaxe d'expression uniquement? Cela réduirait davantage la fréquence des flèches.)
=>
ressemble <=
et >=
:
x => 1 ? 2 : 3
x <= 1 ? 2 : 3
if (x => 1) {}
if (x >= 1) {}
Pour invoquer une expression de fonction flèche immédiatement, il faut placer ()
à l'extérieur, mais placer ()
à l'intérieur est valide et peut être intentionnel.
(() => doSomething()()) // Creates function calling value of `doSomething()`
(() => doSomething())() // Calls the arrow function
Bien que, si l'on écrit (() => doSomething()());
avec l'intention d'écrire une expression de fonction immédiatement invoquée, rien ne se passera.
Il est difficile de prétendre que les fonctions fléchées sont "plus compréhensibles" compte tenu des cas ci-dessus. On pourrait apprendre toutes les règles spéciales requises pour utiliser cette syntaxe. ça en vaut vraiment la peine?
La syntaxe de function
est généralisée sans exception. Utiliser function
exclusivement signifie que le langage lui-même empêche d'écrire du code déroutant. Pour écrire des procédures qui doivent être comprises syntaxiquement dans tous les cas, je choisis function
.
Concernant une ligne directrice
Vous demandez une ligne directrice qui doit être "claire" et "cohérente". L'utilisation des fonctions fléchées aboutira finalement à un code syntaxiquement valide et logiquement invalide, les deux formes de fonction étant entrelacées, de manière significative et arbitraire. Par conséquent, j'offre ce qui suit:
Directive pour la notation des fonctions dans ES6:
- Créez toujours des procédures avec
function
.
- Attribuez toujours
this
à une variable. Ne pas utiliser () => {}
.
Fixed this bound to scope at initialisation
comme une limitation?