我是 React 初学者。this是 JavaScript 学习者的难题,虽然可能已经熟知“它和一般的 OOP 语言不同,this是函数的执行环境/上下文,与它在哪里定义没有关系”这样的理论,但是遇到复杂一些的情况还是会蒙(再加上引入箭头函数后)。
在一般的初学教程里,都会提及一个绑定事件时的this问题。比如在《React 小书》中:
class Title extends Component {
handleClickOnTitle (e) {
console.log(this)
}
render () {
return (
<h1 onClick={this.handleClickOnTitle}>React 小书</h1>
)
}
}
一般在某个类的实例方法里面的
this指的是这个实例本身。但是你在上面的handleClickOnTitle中把this打印出来,你会看到this是null或者undefined。这是因为 React.js 调用你所传给它的方法的时候,并不是通过对象方法的方式调用(
this.handleClickOnTitle),而是直接通过函数调用(handleClickOnTitle),所以事件监听函数内并不能通过this获取到实例。如果你想在事件函数当中使用当前的实例,你需要手动地将实例方法
bind到当前实例上再传入给 React.js。
而程墨在教程中提到,因为每次都要bind挺麻烦的,也可以用类的成员变量和箭头函数来定义。类似于:
class Title extends Component {
handleClickOnTitle = (e) => {
console.log(this)
}
render () {
return (
<h1 onClick={this.handleClickOnTitle}>React 小书</h1>
)
}
}
我现在就是想看看这种情况下直接调用函数时,this是什么情况。
首先,简化一下类的定义:
class A {
constructor() {
this.one = function() {
console.log('one', this);
};
this.two = () => { console.log('two', this); };
}
three() {
console.log('three', this);
}
}
var obj = new A();
可以知道obj.one()/obj.tow()/obj.three(),this全是obj本身。现在改一下:
var aOne = obj.one;
var aTwo = obj.two;
var aThree = obj.three;
aOne(); // one undefined
aTwo(); // two A { one: [Function], two: [Function] }
aThree(); // three undefined
第二个结果依旧是obj本身,原因是箭头函数默认绑定外层this(层:即 js 祖传函数作用域)。在 Babel 中转换为 ES5(我稍微精简了一下,原本是包在一个 IIFE 中的):
function A() {
var _this = this;
this.one = function() {
console.log("one", this);
};
this.two = function() {
console.log("two", _this);
};
}
A.prototype.three = function three() {
console.log("three", this);
};
所以,two中的this并非真正的this,而是一个来自函数外的变量_this,它的值等于this,而这个this在var obj = new A()执行的时候就是obj对象了。
当然类也可以定义成:
class A {
one = function() {
console.log('one', this);
};
two = () => { console.log('two', this); };
three() {
console.log('three', this);
}
}
不过要注意,“类属性”需要 Node.js v12.0 以上才支持,在 Babel 中需要使用 plugin-proposal-class-properties 才能使用。Babel 转换的 ES5 代码略有不同,定义了一个_defineProperty函数,不过本质上还是和上面的代码片段一样的。
结论
ES6 class就是function的语法糖,而箭头函数的this与外层this一致,多外层呢?就是到最近的 function 函数作用域为止的外层。
var a = {
b: () => { //箭头函数不可以作为 constructor!
console.log(this);
},
c: {
d: () => {
console.log(this);
}
},
f: () => {
return () => {
console.log(this); //无论套多少层箭头函数,this 还是最外层的(定义 a 所在的层)this(undefined)
}
},
e: function() {
this.x = 1;
console.log(this);
}
};
a.b(); //undefined/window
a.c.d(); //undefined/window
a.f()(); //undefined/window
a.e(); // {b: f, c: {...}, f: f, e: f, x: 1}, this 是 a,x 添加到了 a 上
new a.e() // e {x: 1}