小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。
React 类式组件中的 this
先来看一个 React 类式组件:
class Weather extends React.Component {
constructor(props) {
super(props)
this.state = {isHot: true}
this.changeWeather = this.changeWeather.bind(this)
}
render() {
const { isHot } = this.state
return <h1 onClick={this.changeWeather}>今天天气很{isHot ? '炎热' : '凉爽'}</h1>
}
changeWeather() {
console.log(this)
}
}
ReactDOM.render(<Weather/>, document.getElementById('test'))
附:
ReactDOM.render方法负责解析找到类,然后 new 出该类的组件实例,并调用其 render 方法。
构造方法中 this.state 哪里来的?
由代码可知 Weather 类继承于 React.Component,在 constructor 方法中打印 this (下图)可以看到 state: null ,说明 state 属性在 React.Component 中的 constructor 方法中已经被定义且初始化赋值为 null。同样 React 初始化的核心属性还有:context、props、refs...
因此我们可以直接在 Weather 类的构造方法里面,使用 this 去访问 state 并且给它赋值。
render 方法中 this.state 解构赋值?
按照 ES6 语法规则 render 方法定义是为实例方法,挂载在 Weather 原型上。
因为 render 方法是由 Weather 类组件实例调用的,所以它的 this 就是 Weather 组件实例,因此可以进行解构赋值,然后访问到该实例构造方法中定义的实例属性值为 isHot: true。
JSX 中的事件绑定 onClick={this.changeWeather}?
render 方法里面返回了 JSX:return <h1 onClick={this.changeWeather}></h1>。有没有想过,为什么不是 return <h1 onClick={changeWeather}></h1>?为什么需要 this 去访问?
这个问题很简单,其实是类的基础知识,changeWeather 方法是 Weather 类的实例方法,因此要通过 this 的方式去调用。
通过 JSX 事件绑定调用的 changeWeather 方法中的 this?
当我们注释掉构造方法中的 this.changeWeather = this.changeWeather.bind(this) 然后点击触发 changeWeather 方法,发现控制台打印出来的 this 为 undefined。这是为什么呢?明明在 render 方法里面打印的 this 是当前实例。
这个属于 this 绑定中的隐式丢失,那什么是 this 的隐式丢失?
let obj = {
a: 42,
foo: function() {
console.log(this.a);
}
};
// 函数别名
let bar = obj.foo;
obj.foo(); // 42
bar(); // undefined
最常见的就是这种函数别名导致的隐式丢失,bar() 相当于 window 调用 foo,window 中没有 a 变量,就会返回 window,那为什么上面返回 undefined 呢?其实是在严格模式下会返回 undefined。(而上面 React 案例中返回 undefined 也是因为 Babel 转换在方法内开启了严格模式。)
JSX 代码中 <h1 onClick={this.changeWeather}>今天天气很{isHot ? '炎热' : '凉爽'}</h1> 的 this.changeWeather 只是通过类的实例对象沿着原型链找到了 changeWeather 方法并交给 onClick 作为回调,此时没有调用这个方法。等真正点击的时候,只是从堆里面把方法拿出来执行。
因此我们需要在构造函数的时候将 this 进行硬绑定:this.changeWeather = this.changeWeather.bind(this)。从右到左分析这个代码,右边的 this.changeWeather 是原型链上,使用 bind 绑定当前实例然后返回一个新函数,并赋值给 this.changeWeather 挂在实例自身的 changeWeather 上。