ES6 中允许使用箭头来定义箭头函数,是 ES6 中较受欢迎也较常使用的新增特性。本文将从箭头函数的基本语法,与普通函数对比,箭头函数不适用场景三个方面进行梳理。
基本语法
1 | // 箭头函数 |
从上面可以看出,定义箭头函数语法上要比普通函数简洁得多。箭头函数省去了function
关键字,采用箭头=>
来定义函数。函数的参数放在=>
前面的括号中,函数体跟在=>
后的花括号中,箭头函数在参数和箭头之间不能换行。
箭头函数的参数
- 如果箭头函数没有参数,直接写一个空括号即可。
- 如果箭头函数的参数只有一个,可以省略包裹参数的括号。
- 如果箭头函数有多个参数,将参数依次用逗号分隔,参数必须被包裹在括号中。
箭头函数的函数体
如果箭头函数的函数体只有一句代码,即返回某个变量或者返回一个简单的 JS 表达式,可以省去函数体的大括号{ }。
1 | let func = val => val |
箭头函数返回一个对象
如果箭头函数的函数体只有一句代码且返回一个对象(对象字面量)时,直接写一个表达式是不行的。
1 | let func = () => { foo: 1 }; |
原因是花括号被解释为函数体的大括号,解决办法:用圆括号把对象字面量包起来
1 | let func = () => ({ foo: 1 }) |
简化回调函数
这是箭头函数比较常见的用法
1 | // 普通函数写法 |
跟普通函数的区别
没有 this 绑定
箭头函数没有自己的 this,它会捕获自己在定义时)所处的外层执行环境的 this,并继承这个 this 值。所以,箭头函数中 this 的指向在它被定义的时候就已经确定了,之后永远不会改变。
1 | const obj = { |
上述代码中,箭头函数与外层的 this 保持一致,最外层的 this 就是 Window 对象。
没有 arguments
1 | function func1(a, b) { |
如果非要打印函数参数,可以在箭头函数中使用 rest 参数代替 arguments 对象
1 | let func2 = (...rest) => { |
不能通过 new 关键字调用
在构造函数中,this 指向新创建的对象实例
而箭头函数没有 [[Construct]]方法,箭头函数不可以当作构造函数,如果这样做会抛出异常
1 | var Person = name => { |
箭头函数在创建时 this 对象就绑定了,故不会指向对象实例。
没有 new.target
new.target 是 ES6 新引入的属性,普通函数如果通过 new 调用,new.target 会返回该函数的引用。
1 | function Cat() { |
此属性主要:用于确定构造函数是否为 new 调用的。
箭头函数的 this 指向全局对象,在箭头函数中使用箭头函数会报错。
1 | // 普通函数 |
没有原型
由于不能通过 new 关键字调用,不能作为构造函数,所以箭头函数不存在 prototype 这个属性。
1 | let func = () => {} |
没有 super
箭头函数没有原型,故也不能通过 super 来访问原型的属性,所以箭头函数也是没有 super 的。同 this、arguments、new.target 一样,这些值由外围最近一层非箭头函数决定。
call/apply/bind 方法无法改变箭头函数中 this 的指向
call()、apply()、bind()方法的共同特点是可以改变 this 的指向,用来动态修改函数执行时 this 的指向。但由于箭头函数的 this 定义时就已经确定了且不会改变。所以这三个方法永远也改变不了箭头函数 this 的指向。
1 | var name = 'global name' |
箭头函数的解析顺序相对靠前
虽然箭头函数中的箭头不是运算符,但箭头函数具有与常规函数不同的特殊运算符优先级解析规则。
1 | let callback; |
箭头函数不支持重名参数
1 | function foo(a, a) { |
1 使用 yield 关键字
yield 关键字通常不能在箭头函数中使用(除非是嵌套在允许使用的函数内)。因此,箭头函数不能用作生成器( Generator )。
箭头函数不适用的场景
1.不应被用在定义对象的方法上
1 | var obj = { |
因为它内部 this 的指向原因,当使用 obj.c()的时候,我们希望 c 方法里面的 this 指向 obj,但是它却指向了 obj 所在上下文中的 this(即 window),违背了我们的需求,所以箭头函数不适合作为对象的方法。
2.具有动态上下文的回调函数,也不应使用箭头函数
1 | var btn = document.getElementById('btn'); |
除了前面两点,剩下的跟上面讲的与普通函数的区别重复了,故只作总结不贴代码了:
- 不应被用在定义对象的方法上
- 具有动态上下文的回调函数,也不应使用箭头函数
- 不能应用在构造函数中
- 避免在 prototype 上使用
- 避免在需要 arguments 上使用