React学习之类组件的this指向问题
1.React学习之类组件的this指向问题
2.React学习之diff算法免责声明
我们幼儿园有适合自己看的注释拉满版文档,目标是我奶来都能看懂(不是)。
1. 前置知识
- 类
- this指向
- call、bind、apply
待展开...欸嘿,我怎么什么都想不己来了
1.1 es6类的简单回顾
class Person { // 构造器 constructor(name, age) { console.log(this); // this 指向类的实例对象(new的作用) this.name = name; this.age = age; } // 一般方法,放在了 Person 的原型对象上,因此实例.say() 沿着原型链可以调用该方法 say() { // 【通过实例】调用 say 时,say 中的 this 指向实例 console.log(`我是${this.name},今年${this.age}`); } } const p1 = new Person('ouo', 18); p1.say(); // 【通过实例】调用 say // ====================================== // 继承 class Student extends Person { constructor(name, age, color) { // 构造器内使用 this 前 super(name, age); // 调用父类的 constructor() this.color = color; } call() { console.log(this); // 实例调用 ,this 指向实例 } // 子类可以重写从父类继承的方法 say() { console.log(`我是${this.name},今年${this.age},喜欢${this.color}色`); } } const c = new Student('kk', 19, 'red'); c.say(); c.toString();
这里是自己 new 实例,实例调用方法;React是帮我们创建类实例对象,用实例对象调用render
2. 类组件的this指向问题
2.1 问题引入
// <script type="text/babel"> // babel 会给整个 script 开启严格模式 // ==== 1. 创建类组件 ==== class Weather extends React.Component { // 构造函数的执行上下文的 this 绑定 类的实例对象,是 new 的作用 // props 是 new Xxx() 传递的,而这个 new 的过程是 React 帮我们做的 // React.Component 的构造器会有一个 值为 null 的 state constructor(props) { // super 必须在使用 this 关键字之前调用,调用了父类的构造器 super(props); console.log(this, '构造函数内的 this 指向类的实例对象'); // 初始化状态 this.state = { isHot: false, wind: '微风', }; } // render 一般会放在 Weather 的 原型对象上 // render 会调用 1 + n 次,1是初始化的那次 n是状态更新的次数 // render内的 this 指向类的实例对象,因为 ReactDOM.render(<Weather />, document.getElementById('test')); React会帮我们 new Weather,并且通过 实例.render调用 render() { console.log(this, 'render内的 this 指向类的实例对象'); const { isHot, wind } = this.state; return ( // 这里是 demo 函数声明提升 + 点击事件回调吧 <h2 onClick={demo}> 今天天气很{isHot ? '炎热' : '凉爽'} </h2> ); } } // 写在 class 外的函数 function demo() { // 在此处修改 isHot的值 this.state.isHot // 首先,这个 demo 不是 DOM 事件绑定的回调!btn.addEventListener('xxx', function (e) {});才是 DOM 事件绑定,回调函数内的 this 才指向绑定的元素 // click 触发后,才执行 demo 函数,本质是 【window】 调用的,指向 window;又由于 babel 开启了严格模式,因此最终指向 undefined console.log(this,'开启严格模式,this 指向 undefined') // this.state.isHot error } // ==== 2. 渲染组件到页面 ==== ReactDOM.render(<Weather />, document.getElementById('test')); // </script>
总结:
- 类的构造器的执行上下文的 this 绑定 类的实例对象,是 new 的作用
- render内的 this 指向类的实例对象,是因为 react 帮我们实例类组件对象后,通过 实例.render 调用
btn.addEventListener('xxx', function (e) {});
才是 DOM 元素事件绑定,回调函数内的 this 才指向绑定的事件源元素;onClick={demo}
是 html 事件绑定,回调函数的 this 指向 window,但 babel 开启了严格模式,因此最终指向 undefined
2.2 解决this指向问题
在构造器内的 this 指向类的实例对象,可以配合 Function.prototype.bind()
,返回一个调用原始函数并将其 this
关键字设置为给定的值的新函数。
// <script type="text/babel"> class Weather extends React.Component { constructor(props) { super(props); console.log(this, '构造函数的 this 指向实例对象'); this.state = { isHot: false, wind: '微风', }; // =========== 解决方式 =========== // this.demo 是沿着 类的实例对象 的原型链调用 demo 方法 // this.demo.bind(this) 返回一个【改变了 this 从 undefined 指向为 类的实例对象】后的 this.demo 函数的拷贝;疑问 bind是深拷贝吗还是创建的新函数,疑似需要去学一下bind,this.demo的 this 并没有改? // this在执行时才确定,所以 this.demo内的 this 并没有改 // this.changeWeather 是在【类的实例上】添加一个方法,赋值 // 至此,再用类的实例调用 changeWeather 方法时,就会就近调用新方法 this.changeWeather = this.demo.bind(this); } render() { const { isHot, wind } = this.state; return ( // 当用户点击按钮时 React 会调用你传递的事件处理函数,这里不必纠结 this.changeWeather 是一个函数而不是一个表达式。 <h2 onClick={this.changeWeather}> 今天天气很{isHot ? '炎热' : '凉爽'} </h2> ); } // =========== 问题所在 =========== // 写在类中,会在类的原型对象上,如果通过 Weather 实例调用,则 this 指向实例对象 // onClick={this.demo} 没有调用demo,而是 click 触发后,才执行实例对象原型链上的这个 demo 函数,本质是 【window】 调用的,指向 window;又由于类中的一般方法默认开启局部严格模式,因此最终指向 undefined // 所以 this.state 会报错,必须有 this.changeWeather = this.demo.bind(this) 先构造改变指向的新方法 + onClick={this.changeWeather} 来解决问题 demo() { console.log(this,'类中的一般方法默认开启严格模式,因此指向 undefined', '但onClick={this.changeWeather}调用的是实例对象的 changeWeather 方法'); // this.changeWeather = this.demo.bind(this) 后,方法体内当成 this.changeWeather 写~ const { isHot } = this.state; this.setState({ isHot: !isHot}); } } ReactDOM.render(<Weather />, document.getElementById('test')); // </script>
总结:
this.changeWeather = this.demo.bind(this);
是在类的实例上添加一个改变了 this 从 undefined 指向为 类的实例对象后的 this.demo 函数的拷贝onClick={this.changeWeather}
在点击后,才触发执行实例对象原型链上的这个 changeWeather 函数- 区分
<h2 onClick={this.changeWeather}>
和<h2 onClick={this.demo}>
打印的 this 不同,即可- React 会调用你传递的事件处理函数,这里不必纠结 this.changeWeather 是一个函数而不是一个表达式
3. state的简写
为什么需要?当有大量事件回调时,需要在构造器内写大量重复代码解决this指向问题。
// <script type="text/babel"> class Weather extends React.Component { // React.Component 的构造器会有一个 值为 null 的 state // 不需要在 new 的时候传参的属性可以省略构造函数直接写赋值语句,这些属性依然会在实例对象身上 state = { isHot: false, wind: '微风', }; render() { const { isHot, wind } = this.state; return ( <h1 onClick={this.changeWeather}> 今天天气很{isHot ? '炎热' : '凉爽'} </h1> ); } // 函数表达式 + 箭头函数 // 表达式 是 类似 state = xxx 的写法,这样实例身上就有了 changeWeather 方法 // 箭头函数的 this 是它声明所在的作用域的 this,即实例对象,因此就不需要在构造器中使用 bind 再改变 this 指向 // why? 指向实例对象 changeWeather = () => { const isHot = this.state.isHot; this.setState({ isHot: !isHot }); }; } ReactDOM.render(<Weather />, document.getElementById('test')); // </script>
总结:
省略构造器 + 表达式 + 箭头函数
疑问: 箭头函数内的 this 指向它所在的作用域的 this binding,箭头函数再往上找是全局执行上下文,即使开了严格模式应该指向 undefined 呀,为什么会指向实例?
因为在 class 中写的函数表达式 + 箭头函数会作为实例的属性,因此箭头函数的上级是构造函数执行上下文, new 的作用指向了实例,因此箭头函数也指向实例。
本文作者:雨宮莲
本文链接:https://www.cnblogs.com/pupyy/p/17717692.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步