这个问题一开始是我在探索在对象上直接声明一个方法时方法内部this的指针到底指向的是对象自己还是顶层对象这个问题引出来的, 先看下面示例:
例子中a.x和a.y的区别就在于是否是箭头函数,输出结果为箭头函数this指向全局,而function声明形式this指向a对象,b和c输出全部指向window。通过上面的结果我们可以得出结论:
1.箭头函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象(这也是es6中的原话),
所以例子中x.a()和b()所输出的结果永远相同。
2.function声明的函数this对象指向的是函数使用时所在的对象,所以a.y()输出a对象而c()输出window。
那么在构造函数里使用箭头函数跟使用function声明构造函数的方法两者有什么区别呢?
于是我在react项目中做了一个测试(class内部使用箭头函数属于实验性功能,react项目里使用了@babel/plugin-proposal-class-properties插件,所以支持使用实验性的属性初始化器语法,也可以在任何支持该方法的环境中使用):
输出结果如下:
a.x()和b.x()分别采用箭头函数,但是输出一个是a对象,一个是test对象(test对象为react组件构造函数,即当前环境的顶层对象),而a.y()和b.y()分别指向a对象和b对象,为正常输出,以此我们可以得出结论:
1.es5创建构造函数的方法不能用箭头函数,这将导致方法内部的this无法指向构造函数创建出来的对象,这也对应了上面提到过的箭头函数的描述(箭头函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象)。
2.es6构造函数方法采用实验性属性初始化器语法,我们可以在class内部采用箭头函数创建方法(react文档中也有提到可以在组件中采用该技术),方法内部的this可以正常指向构造函数创建出来的对象。
但是仔细看了一下输出,发现a.y()和b.y()的输出有些不太一样:
可以看出来a.x方法是直接写在a对象上的,而b.x是在b对象原型上的,a.y和b.y都是在原型上的,所以本质上在class里面使用箭头函数声明的方法并不是原型方法,而是后来赋上去的,这跟在constructor内部通过this.x = this.x.bind(this)运行出来的结果也不一样(this指向结果一致,原理不一致),还有待解析…
更新:
针对上面行为的不同,比本人有以下见解:
在某些特定情况下(react环境中、构造函数的方法在外部调用),非箭头函数原型方法中的this会指向使用时所在对象导致无法获取原型中的this,这个时候原型方法必须绑定构造函数的this,则必须使用箭头函数或者bind方法将this绑定在构造函数上面,只不过原型方法将变成构造函数实例直接的方法,不再是原型方法,可以理解为每次实例化时将this强行绑定在该实例上,如果还是在原型上共享给所有实例的话,那么怎么绑定this呢;
当然变成实例的方法后,每new一个实例就会重新创建一个原型方法,因为不在原型上无法共享了嘛,存在性能上的缺陷,但目前来看也是不可避免的。