箭头函数与普通函数的区别, 实质是我们是否理解了箭头函数, 在我刚开始接触 ES6 时, 印象中的箭头函数与普通函数的区别就是 this 的指向不同, 一个指向 Windows, 一个指向当前的实例, 作用域. 可箭头函数远远不止这么一个知识点, 于是重新学习箭头函数:
JavaScript 里, this 的值在函数被调用的时候才会指定. 顶级的非方法式调用会将 this 视为 Windows. (注意: 在严格模式下, this 为 undefined 而不是 Windows). 箭头函数能保存函数创建时的 this 值, 而不是调用时的值. 这是来自 ts 官网的解释.
箭头函数
说明
箭头函数表达式的语法比函数表达式更简洁, 并且没有自己的 this,arguments,super 或 new.target. 这些函数表达式更适用于那些本来需要匿名函数的地方, 并且它们不能用作构造函数.
箭头函数的 this
1. 箭头函数不会创建自己的 this, 它只会从自己的作用域链的上一层继承 this. 箭头函数的 this 指向在定义的时候继承自外层第一个普通函数的 this.
- function Person(){
- this.age = 0;
- setInterval(() => {
- this.age++; // |this| 正确地指向 p 实例
- }, 1000);
- }
- var p = new Person();
2. 如果箭头函数外层没有普通函数, 在非严格模式下, 默认绑定的 this 指向全局对象, 严格模式下 this 指向 undefined
- var obj = {
- i: 10,
- b: () => console.log(this.i, this),
- c: function() {
- console.log( this.i, this)
- }
- }
- obj.b();
- // undefined, Windows{...}
- obj.c();
- // 10, Object {...}
箭头函数没有 prototype(原型), 所以箭头函数本身没有 this
- let obj = () =>{
- };
- console.log(obj .prototype); // undefined
通过 call 或 apply 调用, 不能直接修改箭头函数的 this 指向
由于 箭头函数没有自己的 this 指针, 通过 call() 或 apply() 方法调用一个函数时, 只能传递参数(不能绑定 this), 他们的第一个参数会被忽略.
- var adder = {
- base : 1,
- add : function(a) {
- var f = v => v + this.base;
- return f(a);
- },
- addThruCall: function(a) {
- var f = v => v + this.base;
- var b = {
- base : 2
- };
- return f.call(b, a);
- }
- };
- console.log(adder.add(1)); // 输出 2
- console.log(adder.addThruCall(1)); // 仍然输出 2(而不是 3 )
箭头函数的 arguments, 箭头函数不绑定 Arguments 对象, 也就是说 arguments 也没有
注意: 箭头函数的 this 指向普通函数时, 它的 argumens 继承于该普通函数
- function foo() {
- console.log(arguments) // { '0': 1, '1': 2 }
- return function() {
- console.log(arguments[0]) // { '0': 3, '1': 4 }
- }
- }
- foo(1, 2)(3, 4) // 3
REST 参数获取函数的剩余参数, 在大多数情况下, 使用剩余参数是相较使用 arguments 对象的更好选择.
REST 参数的用法相对于 arguments 的优点:
箭头函数和普通函数都可以使用.
更加灵活, 接收参数的数量完全自定义.
REST 参数的操作符表示为 3 个点 .... 直白地讲, 它的意思就是 "把剩余的参数都放到一个数组中".
- let a = (first, ...REST) => {
- console.log(first, REST); // 1 [2, 3, 4]
- };
- a(1, 2, 3, 4);
- function foo() {
- console.log(arguments) // { '0': 1, '1': 2 }
- return () => console.log(arguments[0])
- }
- foo(1, 2)(3, 4) // 1
使用 new 操作符
箭头函数不能用作构造器, 和 new 一起用会抛出错误, 因为箭头函数没有 constructor.
- var Foo = () => {
- };
- var foo = new Foo(); // TypeError: Foo is not a constructor
箭头函数的使用, 比普通函数更加简洁
更简短的函数并且不绑定 this
1. 使回调函数看起来更加优雅
2. 只有一个参数的时候可以省略括号
3. 函数只有一条语句时可以省略 {} 和 return
4. 箭头函数都是匿名函数, 并且都不用写 function
- [8, 6, 7, 9].map(function(item) {
- return item;
- });
- [8, 6, 7, 9].map(item => item;);
使用 yield 关键字
yield 关键字通常不能在箭头函数中使用(除非是嵌套在允许使用的函数内). 因此, 箭头函数不能用作生成器
例子(面试常考题)
- var id = 'global';
- var obj = {
- id: 'item',
- a: function(){
- console.log(this.id);
- },
- b: () => {
- console.log(this.id);
- }
- };
- obj.a(); // 'OBJ'
- obj.b(); // 'global'
上面这个例子, 对象 obj 的方法 a 使用普通函数定义的, 普通函数作为对象的方法调用时, this 指向它所属的对象. 所以, this.id 就是 obj.id, 所以输出'item'.
但是方法 b 是使用箭头函数定义的, 箭头函数中的 this 实际是继承的它定义时所处的全局执行环境中的 this, 所以指向 Windows 对象, 所以输出'global'.(这里要注意, 定义对象的大括号 {} 是无法形成一个单独的执行环境的, 它依旧是处于全局执行环境中!!)
文章小结
箭头函数有几个使用注意点.
(1)函数体内的 this 对象, 就是定义时所在的对象, 而不是使用时所在的对象.
(2)不可以当作构造函数, 也就是说, 不可以使用 new 命令, 否则会抛出一个错误.
(3)不可以使用 arguments 对象, 该对象在函数体内不存在. 如果要用, 可以用 REST 参数代替.
(4)不可以使用 yield 命令, 因此箭头函数不能用作 Generator 函数.
(5)不要在最外层去定义箭头函数, 至少箭头函数外部要包一层普通函数.
(6)箭头函数的 this 意外指向和代码的可读性.
什么情况下使用箭头函数
无复杂逻辑或者无副作用的纯函数场景下, 例如 map,forEach 回调中使用.
箭头函数使用中注意事项
1. 返回对象字面量, 记得用圆括号把对象字面量包起来:
2. 换行, 箭头函数在参数和箭头之间不能换行.
3. 解析顺序, 箭头函数的解析顺序相对 || 靠前
4. 在一个简写体中, 只需要一个表达式, 并附加一个隐式的返回值. 在块体中, 必须使用明确的 return 语句.
- var func = () => ({foo: 1});
- var func = ()
- => 1;
- // SyntaxError: expected expression, got '=>'
- callback = callback || (() => {});
- var func = (x, y) => { return x + y; };
来源: http://www.jianshu.com/p/8dc262ec6506