一种更简短的函数写法
箭头函数是 JavaScript 中定义函数的一种更简洁的语法。它出现在 ES2015 中,如今已经是小型内联函数的默认写法,尤其是传给数组方法的回调、Promise 处理函数和事件监听器。
它的结构是:参数,然后是 =>,再然后是函数体。
和传统写法的结果完全一样:
const add = function (a, b) {
return a + b;
};
少打几个字固然不错,但真正值得关注箭头函数的原因,是隐式返回以及它们对 this 的处理方式。下面就来讲这两点。
隐式返回
如果函数体只有一个表达式,你可以省略花括号和 return。表达式的值会被自动返回:
这正是箭头函数的用武之地:单行的转换读起来就像数学公式:
一旦需要多条语句,你就得回到花括号和显式的 return:
在花括号里忘写 return 是箭头函数最常见的错误。x => { x * 2 } 返回的是 undefined:花括号把函数体变成了一个代码块,里面的表达式结果被直接丢掉了。
参数的括号
只有一个参数时,括号可以省略:
没有参数,或者有两个及以上参数时,就必须写括号:
有些团队为了统一,喜欢始终加括号。两种风格都可以,选定一种并坚持下去就好。
返回对象字面量
这里有个坑。如果你想隐式返回一个对象,它的花括号看起来就像函数体:
这会打印 undefined。JavaScript 把 { name: name } 理解成一个包含带标签语句的代码块,而不是对象字面量。用括号把对象包起来,强制让它成为一个表达式:
在 { ... } 外面加上 () 就解决了。记住这一点:开始写箭头函数后的第一周内,你就会遇到它。
词法 this
箭头函数存在的真正原因并不是语法更短,而是它们没有自己的 this。它们从外层作用域继承 this。
为了理解这一点为什么重要,来看一个用作回调的普通函数:
在传给 setInterval 的 function () { ... } 内部,this 不是 counter。普通函数的 this 取决于它被调用的方式,而 setInterval 调用回调时,this 是 undefined(严格模式下)或全局对象。
箭头函数则会保留外层方法的 this:
在箭头函数内部,this 依然是 counter,因为箭头函数不会引入自己的 this。在有箭头函数之前,人们用 const self = this; 或 .bind(this) 来绕过这个问题。这些写法依然有效,但现在已经很少需要了。
箭头函数没有的东西
词法 this 规则只是一个更大规律的一部分:普通函数拥有的好几样东西,箭头函数都没有。
- 没有自己的
this:从外层作用域继承。 - 没有
arguments对象:改用剩余参数(...args)。 - 不能用
new:箭头函数不能用作构造函数。 - 没有
prototype属性:这是不能用new带来的结果。
...args 提供了和 arguments 一样的“接收任意数量参数”的能力,而且它还是一个真正的数组。new Greeter(...) 这次调用会抛出错误,因为箭头函数不是构造函数。
什么时候不该用箭头函数
箭头函数是回调的默认选择,但在少数场合它并不合适:
用箭头语法定义的对象方法,this 不会绑定到这个对象上,而是继承自对象字面量所在的位置(通常是模块或全局作用域)。定义方法时,请使用简写方法语法(greet() { ... })。
同样的道理也适用于类的原型方法、需要用 this 指向元素的事件处理方法,以及任何你打算用 new 调用的函数。这些地方应该用普通的 function。
简单的经验法则
- 简短的回调,只有一个表达式? 用箭头函数。
- 需要
this保持绑定在外层作用域? 用箭头函数。 - 在对象或类的原型上定义方法? 用普通函数(或简写方法)。
- 编写构造函数? 用普通函数,更好的选择是
class。
在任何真实的 JavaScript 代码库中,你都会看到这两种写法。读上几周别人的代码,你就会知道哪种更合适,之后这种选择就会变成本能。
下一页:参数与默认值
箭头函数和普通函数共享同样的参数特性:默认值、剩余参数、解构参数等等。这就是下一页的内容,它适用于你目前见过的所有函数形式。
常见问题
JavaScript 中的箭头函数是什么?
箭头函数是一种更简短的函数写法,使用 => 语法。const add = (a, b) => a + b; 就是一个箭头函数,它接收两个参数并返回它们的和。除了语法更简洁,箭头函数不会绑定自己的 this、arguments 或 super,而是从外层作用域继承它们。
箭头函数和普通函数有什么区别?
用 function 声明的普通函数有自己的 this 和自己的 arguments 对象,还可以配合 new 用作构造函数。箭头函数这些都没有,它的 this 继承自定义它的地方。另外,箭头函数也不能像函数声明那样被提升。
什么时候应该用箭头函数?
简短的回调函数(arr.map(x => x * 2))适合用箭头函数;凡是希望 this 保持绑定在外层作用域的地方也适合,比如在类方法里把处理函数传给 setTimeout 或事件监听器时。对象上的方法、构造函数和独立的顶层函数,则用普通的 function。