前言
ES6 中带来了更简洁写法的箭头函数,小伙伴们可不要为了图简洁而掉入坑中。本文详细带大家理解箭头函数。
官方定义
箭头函数表达式的语法比函数表达式更简洁,并且没有自己的this,arguments,super或new.target。箭头函数表达式更适用于那些本来需要匿名函数的地方,并且它不能用作构造函数。
引入箭头函数有两个方面的作用:更简短的函数并且不绑定
this。后文将按照这两方面来理解箭头函数。
箭头函数和普通函数的this
在箭头函数出现之前,每一个新函数根据它是被如何调用的来定义这个函数的 this 值:
- 如果该函数是一个构造函数,this 指针指向一个新的对象
- 在严格模式下的函数调用下,this 指向
undefined;非严格模式,this 指向window或global - 如果该函数是一个对象的方法,则它的 this 指针指向这个对象
- 等等
示例:
function Person() {
// Person() 构造函数定义 `this`作为它自己的实例。
this.age = 0;
setInterval(function growUp() {
// 在非严格模式,growUp() 函数定义 `this`作为全局对象,
// 与在 Person() 构造函数中定义的 `this`并不相同。
this.age++;
console.log(this.age) // NaN NaN ...
}, 1000);
}
var p = new Person();
而箭头函数不会创建自己的this,它只会从自己的作用域链的上一层继承 this
function Person() {
// Person() 构造函数定义 `this`作为它自己的实例。
this.age = 0;
setInterval(() => {
this.age++; // |this| 正确地指向 p 实例
console.log(this.age) // 1 2 3 ...
}, 1000);
}
var p = new Person();
有人说箭头函数的this是在定义的时候就确定好了的,这样理解也没错。因为函数的作用域是创建或者说定义的时候就确定好了,而this继承自己作用域链的上一层。
Tip
箭头函数的
this只在作用域链的上一层找,而不会沿着作用域链一直找。
示例:
function Person() {
// Person() 构造函数定义 `this`作为它自己的实例。
this.age = 0;
function Child() {
this.name = '123'
setInterval(() => {
// 在非严格模式,growUp() 函数定义 `this`作为全局对象,
// 与在 Person() 构造函数中定义的 `this`并不相同。
this.age++;
console.log(this.age) // NaN ...
console.log(this.name) // 123 ...
}, 1000);
}
Child()
}
var p = new Person();
箭头函数的 this 继承自Child,这个this不会像变量一样会沿着作用域一直找。
Tip
通过 call 、 apply 、 bind 调用时,只能传递参数(不能绑定 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
Tip
如何抉择,什么时候使用普通函数,什么时候使用箭头函数?我们看一个
Object.defineProperty的例子。
'use strict';
var obj = {
a: 10
};
Object.defineProperty(obj, "b", {
get: () => {
console.log(this.a, typeof this.a, this);
return this.a + 10;
// 代表全局对象 'Window', 因此 'this.a' 返回 'undefined'
}
});
obj.b; // undefined "undefined" Window {postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, frames: Window, …}
上述箭头函数换成普通函数,this就会指向obj。其实,使用何种函数取决于你对函数内 this 的需求。如果你需要this指向调用的对象,那就用普通函数。其他场景基本可以用箭头函数取代普通函数,毕竟箭头函数是很简洁的。
更简短的函数
省略 return
var func = x => x * x;
// 简写函数 省略 return
返回对象字面量
var func = () => ({foo: 1});
闭包
var Add = (i = 0) => () => (++i);
返回了一个函数 () => (++i),该函数包含了对外部变量的引用。
箭头函数的限制
没有arguments
示例:
const add = (x) => console.log(arguments[0]) // Uncaught ReferenceError: arguments is not defined
add(1)
隐式绑定arguments:
function foo(n) {
// 隐式绑定 foo 函数的 arguments 对象。arguments[0] 是 n,即传给 foo 函数的第一个参数
var f = () => arguments[0] + n;
return f();
}
使用剩余参数:
function foo(arg1, arg2) {
var f = (...args) => args[1];
return f(arg1, arg2);
}
foo(1, 2); //2
不能作为构造器
和 new一起用会抛出错误。
var Foo = () => {};
var foo = new Foo(); // TypeError: Foo is not a constructor
没有 prototype 属性
var Foo = () => {};
console.log(Foo.prototype); // undefined
不能用作函数生成器
yield关键字通常不能在箭头函数中使用(除非是嵌套在允许使用的函数内)。