phenomLi / Blog

Comments, Thoughts, Conclusions, Ideas, and the progress.
219 stars 17 forks source link

ES6箭头函数的坑 #2

Open phenomLi opened 6 years ago

phenomLi commented 6 years ago

箭头函数() => ()是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');
    }

});

至于具体原因,上面也解释得很清楚了,就是上下文得问题,至于解决方法,目前只有两个:

  1. 不要用箭头函数,用回常规的function
  2. 使用ES6class语法定义React组件