免责声明
我们幼儿园有适合自己看的注释拉满版文档,目标是我奶来都能看懂(不是)。
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.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>
总结:
标签:changeWeather,调用,指向,demo,isHot,React,实例,组件 From: https://www.cnblogs.com/pupyy/p/17717692.html省略构造器 + 表达式 + 箭头函数