Blog
Blog copied to clipboard
ES6箭头函数的坑
箭头函数() => ()
是ECMAScript2015中代替function
的一个语法糖,他增强了JS中Functional programming的能力。箭头函数与传统的functuon(){}
声明并没有什么区别,但是在表现上,还是有一点微小区别的,而且这微小区别如果处理不好就会出现大bug。
先看看官方对箭头函数与function的区别的定义:
1、对 this 的关联。函数内置 this 的值,取决于箭头函数在哪儿定义,而非箭头函数执行的上下文环境。 2 、new 不可用。箭头函数不能使用 new 关键字来实例化对象,不然会报错。 3、this 不可变。函数内置 this 不可变,在函数体内整个执行环境中为常量。 4、没有arguments对象。更不能通过arguments对象访问传入参数。只能使用显式命名或其他ES6新特性来完成。
第四点不重要,重要的是前三点,简单来说,就是箭头函数和function对内部的this的处理是不一样的。function内部的this由function决定,而箭头函数的this则由上下文决定。下面来看一个小例子:
const obj = {
a: function() {
console.log(this);
},
b: () => {
console.log(this);
}
};
obj.a(); //输出obj
obj.b(); //输出window
可以看到在箭头函数中,this指针指向的是全局变量window。 为什么会这样呢,其实上面js字面量的写法就等同于下面这种:
const obj = {};
obj.a = function() {
console.log(this);
};
obj.b = () => {
console.log(this);
};
这时外层上下文并不是obj而是window,所以箭头函数里面this就指向了window。
有时候这种特性会很方便,比如,我们要在setTimeout
函数里面获取当前对象,如果这样写:
const obj = {
fn: function() {
setTimeout(function() {
console.log(this); //window
}, 0);
}
};
会发现打印出来的值是window对象而不是obj,因为setTimeout
的真正写法是window.setTimeout
此时setTimeout
里面的上下文是window,打印出来的自然是window。
但是如果想要在setTimeout
里面获得obj该怎么办呢,一种老的hack写法是这样:
const obj = {
fn: function() {
const _this = this; //保存当前上下文
setTimeout(function() {
console.log(_this);
}, 0);
}
};
老的写法是把当前this保存在一个变量里,但是这种写法毕竟是一种hack。现如今有了箭头函数,我们可以这样做:
const obj = {
fn: function() {
setTimeout(() => { //使用箭头函数
console.log(this); //obj
}, 0);
}
};
因为箭头函数中的this根据的是外层上下文,而在setTimeout
函数中内层是window,外层是obj,所以可以轻松拿到正确的值。
之前在写react的过程中,就遇到了箭头函数的一些坑,如在React.createClass
方法中,最好不要用箭头函数来声明方法:
const trank = React.createClass({
handler: () => {
console.log('now the pointer "this" is ' + this + '.'); //点击div,打印出的this是window
this.setState(); //同时setState方法也会无效
},
render: function() {
return React.createElement('div', {onClick: this.handler}, 'hello');
}
});
至于具体原因,上面也解释得很清楚了,就是上下文得问题,至于解决方法,目前只有两个:
- 不要用箭头函数,用回常规的function
- 使用ES6class语法定义React组件