class中的arrow function

690 阅读2分钟

前言

得益于class properties proposal,React的代码变得简洁了许多。在React中处理this绑定的时候,也习惯性地使用属性声明来替代过去constructor赋值的方式

class A extends React.Component {
    constructor(props) {
        super(props);
        this.handleClick = this.handleClick.bind(this);
    }
    handleClick() {
        // do something
    }
}

// arrow function
class B extends React.Component {
    handleClick = () => {
       	// do something
    }
}

箭头函数在声明属性的时候自动绑定 this,省去了在constructor中的函数覆盖操作。

但是使用arrow function真的就一本万利了吗?

class中的arrow function

// code 1
class Base {
    commonHandler = () => {
        // do something
        console.log('Base commonHandler', this.text);
    }
    text = 'base'
	selfHandler() {
    	console.log('selfHandler')
	}
}

class A extends Base {
    text = 'A'
	commonHandler(){
    	console.log('A commonHandler', this.text);
    }
}
const a = new A();
a.commonHandler(); // Base commonHandler A

上述代码最终的执行会有点让人意外,为什么text属性发生了覆盖,而commonHandler的函数却还是执行父类?

我们通过babel编译来先来看下这段代码在es2017下的结果

class Base {
    constructor() {
        // commonHandler被移到了constructor中
        this.commonHandler = () => {
            // do something
            console.log('excute commonHandler', this.text);
        };

        this.text = 'base';
    }
    selfHandler() {
        console.log('selfHandler');
    }
}

可以看到text属性和箭头函数commonHandler全被移到constuctor中,而正常成员方法selfHandler则没有改变。es6的class中可以通过类似寄生组合式继承进行模拟,编译结果

class A执行继承的后在constructor中将会调用父类的构造函数,而成员方法commonHandler挂载到了A的prototype上,实例化后按照方法查找的顺序,先获取的是实例上的方法。可以通过将code 1中的a.commonHandler()替换为a.__proto__.commonHandler()输出了预期覆盖父类方法的的结果'A commonHandler A'。

补充:class中的super在作为对象使用时,在普通方法中指向的是父的原型对象,所以如果父类的方法是通过arrow function定义fn,则该方法在子类中将无法通过类似super.fn()进行调用

滥用arrow function的影响

那我们是不是可以在子类中也通过arrow function对commonHandler进行绑定以达到覆盖的目的?

就结果而言的确这样做,可以根据上述对arrow function的分析,每指定一个arrow function都将会在contructor中出现,也就意味着多个地方实例化,将会创建多个方法,而不是像通常定义在原型上面,这样定义的方法在所有的实例上共享。

那是不是意味着我们要抛弃使用arrow function,答案是否定,我们这里要杜绝的是滥用,仅仅做有必要绑定的。比如onClick={this.doSomething}fetch.then(this.hanldeDone)

附上一个优化方案:autobind-decorator核心思路是仅在必要时绑定作用域

参考: Arrow Functions in Class Properties Might Not Be As Great As We Think