根据该提案,箭头旨在“解决传统函数表达式的几个常见痛点”。他们打算通过在词汇上绑定并提供简洁的语法来改善这一问题。
然而,
不能始终在词汇上绑定它
箭头函数的语法是微妙和模糊的
因此,箭头函数容易引起混淆和错误,应该从JavaScript程序员的词汇表中排除,而专门用函数代替。
关于词汇this
这是有问题的:
function Book(settings) {
this.settings = settings;
this.pages = this.createPages();
}
Book.prototype.render = function () {
this.pages.forEach(function (page) {
page.draw(this.settings);
}, this);
};
箭头函数旨在解决需要在回调中访问this的属性的问题。已经有几种方法可以做到这一点:可以将它赋值给一个变量,使用bind,或者使用Array聚合方法上可用的第三个参数。然而,箭头似乎是最简单的解决方法,所以该方法可以像这样重构:
this.pages.forEach(page => page.draw(this.settings));
但是,考虑一下代码是否使用了像jQuery这样的库,其方法是专门绑定的。现在,有两个这个值需要处理:
Book.prototype.render = function () {
var book = this;
this.$pages.each(function (index) {
var $page = $(this);
book.draw(book.currentPage + index, $page);
});
};
我们必须使用函数来动态地绑定每个对象。这里不能用箭头函数。
处理多个this值也会令人困惑,因为很难知道作者在说哪个:
function Reader() {
this.book.on('change', function () {
this.reformat();
});
}
作者真的打算调用Book.prototype.reformat吗?或者他忘记绑定这个,并打算调用Reader.prototype.reformat?如果我们将处理程序更改为一个箭头函数,我们同样会怀疑作者是否想要动态this,但选择了一个箭头,因为它适合一行:
function Reader() {
this.book.on('change', () => this.reformat());
}
有人可能会问:“箭头有时可能是错误的功能,这是例外吗?也许如果我们只是很少需要动态这个值,那么大多数时候使用箭头仍然是可以的。”
但是问问你自己:“调试代码并发现错误的结果是由‘边缘情况’引起的,这样做‘值得’吗?’”我宁愿避免麻烦,不只是大部分时间,而是100%的时间。
有一个更好的方法:总是使用函数(这样总是可以动态绑定),并且总是通过变量引用它。变量是词法的,有很多名字。将它赋值给一个变量会让你的意图更明确:
function Reader() {
var reader = this;
reader.book.on('change', function () {
var book = this;
book.reformat();
reader.reformat();
});
}
此外,始终将this分配给一个变量(即使只有一个this或没有其他函数)可以确保即使在代码更改之后,意图仍然清晰。
此外,动态也并非例外。jQuery在超过5000万个网站上使用(截至2016年2月撰写本文时)。下面是其他动态绑定的api:
Mocha(昨天大约有12万下载量)通过这个公开了它的测试方法。
Grunt(昨天大约有63000次下载)通过此方法公开了构建任务的方法。
Backbone(昨天下载了大约22k)定义了访问它的方法。
事件api(像DOM一样)用这个引用EventTarget。
打了补丁或扩展的原型api引用的是带有这个的实例。
(统计数据可浏览http://trends.builtwith.com/javascript/jQuery和https://www.npmjs.com。)
您可能已经需要动态的此绑定。
这个词有时是预期的,但有时不是;就像动态一样,这有时是预期的,但有时不是。值得庆幸的是,有一种更好的方法,它总是生成并传递预期的绑定。
关于简洁的语法
箭头函数成功地为函数提供了“更短的语法形式”。但这些更短的职位会让你更成功吗?
是否x => x * x“比函数(x) {return x * x更容易阅读”;} ?也许是这样,因为它更有可能生成单一的短行代码。根据戴森的《阅读速度和线长对屏幕阅读效果的影响》,
中等行长(每行55个字符)似乎支持在正常和快速速度下有效阅读。这产生了最高水平的理解……
条件(三元)操作符和单行if语句也有类似的理由。
然而,你真的在写提案中宣传的简单数学函数吗?我的域不是数学的,所以我的子例程很少如此优雅。相反,我经常看到箭头函数打破了列的限制,由于编辑器或样式指南的原因而换行到另一行,这就取消了Dyson定义的“可读性”。
有人可能会提出,“如果可能的话,如何只使用简短的版本来处理简短的函数?”但现在,一个风格规则与语言约束相矛盾:“尽量使用最短的函数符号,记住,有时只有最长的符号才能按预期绑定它。”这种合并使得箭头特别容易被误用。
箭头函数的语法有很多问题:
const a = x =>
doSomething(x);
const b = x =>
doSomething(x);
doSomethingElse(x);
这两个函数在语法上都是有效的。但doSomethingElse (x);不在b的主体中。它只是一个缩进不好的顶级语句。
当扩展为块形式时,不再有隐式的返回,可能会忘记恢复。但是这个表达式可能只是为了产生副作用,所以谁知道接下来是否需要显式返回呢?
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);
};
rest形参可以解析为展开运算符:
processData(data, ...results => {}) // Spread
processData(data, (...results) => {}) // Rest
赋值可能与默认实参混淆:
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 parentheses
块看起来像对象:
(id) => id // Returns `id`
(id) => {name: id} // Returns `undefined` (it's a labeled statement)
(id) => ({name: id}) // Returns an object
这是什么意思?
() => {}
作者打算创建一个无操作的函数,还是一个返回空对象的函数?(考虑到这一点,我们是否应该放置{after =>?我们是否应该只使用表达式语法?这将进一步降低箭头的频率。)
=>看起来像<=和>=:
x => 1 ? 2 : 3
x <= 1 ? 2 : 3
if (x => 1) {}
if (x >= 1) {}
要立即调用箭头函数表达式,必须将()放置在外部,而将()放置在内部是有效的,可能是有意的。
(() => doSomething()()) // Creates function calling value of `doSomething()`
(() => doSomething())() // Calls the arrow function
虽然,如果有人写(()=> doSomething()());为了编写一个立即调用的函数表达式,什么也不会发生。
考虑到以上所有情况,很难说箭头函数“更容易理解”。您可以学习使用此语法所需的所有特殊规则。这真的值得吗?
函数的语法是非常一般化的。专门使用函数意味着语言本身可以防止编写令人困惑的代码。为了编写在所有情况下都能从语法上理解的过程,我选择了函数。
关于指导方针
你要求一个需要“清晰”和“一致”的指导方针。使用箭头函数最终会导致语法有效,逻辑无效的代码,两种函数形式交织在一起,有意义且任意。因此,我提出以下建议:
ES6函数表示法指南:
总是创建带有函数的过程。
总是把这个赋值给一个变量。不要使用()=>{}。