我发现了华点(vue规定用普通函数定义方法,为什么react又要我用箭头函数!)
原文首发在我的公众号,订阅可第一时间查看我的最新文章!
大家好,我是年年!
如果使用过react和vue,应该发现过一个问题:vue告诉我们不应该把方法、生命周期用箭头函数去定义;而在react的类组件中,把方法写成箭头函数的形式却更方便。
要问其原因,大部分人都只把他当一个理所当然的规定。但把这个问题剖开,其实能很好地把准备面试时造的火箭,在拧螺丝的时候用起来。
这篇文章可以让你在这个实际场景中去用到this的指向、作用域链以及原型。
this指向丢失
无论是vue还是react,都在官方文档中强调,需要注意this的指向丢失。但有趣的是,为了达到同样的目的,一个是不能使用箭头函数,一个是使用箭头函数便能解决
react
文章图片
vue
文章图片
React中this的丢失
首先来看看react,这是一个很普通的类组件写法:
class Demo extends React.Component{
state = {
someState:'state'
}
// ?推荐
arrowFunMethod = () => {
console.log('THIS in arrow function:',this)
this.setState({someState:'arrow state'})
}
// ?需要处理this绑定
ordinaryFunMethod(){
console.log('THIS oridinary function:',this)
this.setState({someState:'ordinary state'})
}
render(){
return ( {this.state.someState}
)
}
}
ReactDOM.render( ,document.getElementById('root'))
我在组件内我定义了两个方法:一个用箭头函数实现,另一个用普通函数。在调用时分别打印
this
,结果如下:文章图片
箭头函数中this正确指向了组件实例,但普通函数中却指向了undefined,为什么?
其实这是一个无关react的js特性,剥离react带来的心智负担,本质上,上面的代码不过是一个「类」,简化一下,就变成了这样:
class ReactDemo {
// ?推荐
arrowFunMethod = () => {
console.log('THIS in arrow function:', this)
}
// ?this指向丢失
ordinaryFunMethod() {
console.log('THIS in oridinary function:', this)
}
}
const reactIns = new ReactDemo()
let arrowFunWithoutCaller = reactIns.arrowFunMethod
let ordinaryFunWithoutCaller = reactIns.ordinaryFunMethod
arrowFunWithoutCaller()
ordinaryFunWithoutCaller()
运行一下上面这段代码,会发现结果不出预料:在普通函数中this的指向也丢失了。
文章图片
从react代码运行的角度来解释一下:
首先是事件触发时,回调函数的执行。回调函数不是像这样直接由实例调用:
reactIns.ordinaryFunMethod()
,而是像上面代码中的,做了一次“代理”,最后被调用时,找不到调用对象了:ordinaryFunWithoutCaller()
。这时就出现了this指向undefined的情况。但为什么使用箭头函数,this又可以正确指向组件实例呢?首先回顾一个简单的知识点:class是个语法糖,本质不过是个构造函数,把上面的代码用它最原始的样子写出来:
'use strict'
function ReactDemo() {
// ?推荐
this.arrowFunMethod = () => {
console.log('THIS in arrow function:', this)
}
}
// ?this指向丢失
ReactDemo.prototype.ordinaryFunMethod = function ordinaryFunMethod() {
console.log('THIS in oridinary function:', this)
}
const reactIns = new ReactDemo()
可以看到:写成普通函数的方法,是被挂载到原型链上的;而使用箭头函数定义的方法,直接赋给了实例,变成了实例的一个属性,并且最重要的是:它是在「构造函数的作用域」被定义的。
我们知道,箭头函数没有自己的this,用到的时候只能根据作用域链去寻找最近的那个。放在这里,也就是构造函数这个作用域中的
this
——组件实例。这样就可以解释为什么react组件中,箭头函数的this能正确指向组件实例。
vue中this的丢失 把上面的组件用vue来写一遍:
const Demo = Vue.createApp({
data() {
return {
someState:'state',
}
},
methods:{
// ?this指向丢失
arrowFunMethod:()=>{
console.log('THIS in arrow function:',this)
this.someState = 'arrow state'
},
// ?推荐
ordinaryFunMethod(){
console.log('THIS in oridinary function:',this)
this.someState = 'ordinary state'
}
},
template:`{{this.someState}}
`
})
Demo.mount('#root')
运行代码,会发现结果对调了:使用箭头函数反而导致了this指向丢失:this指向了window对象
文章图片
这部分解释起来会稍微复杂一下,不过也只涉及一小块vue源码。主要的操作是vue对组件方法的处理,最核心的就三行,感兴趣的可以去看看完整代码:vue-github
function initMethods(vm: Component, methods: Object) {
for (const key in methods) {
vm[key] = bind(methods[key], vm)
}
}
vue会把我们传入
methods
遍历,再一个个赋给到组建实例上,在这个过程就处理了this的绑定(bind(methods[key], vm)
):把每一个方法中的this都绑定到组件实例上。普通函数都有自己的this,所以绑定完后,被调用时都能正确指向组件实例。但箭头函数没有自己的this,便无从谈及修改,它只能去找父级作用域中的this。这个父级作用域是谁呢?是组件实例吗?我们知道作用域只有两种:全局作用域和函数作用域。回到我们写的vue代码,它本质就是一个对象(具体一点,是一个组件的配置对象,这个对象里面有data、mounted、methods等属性)也就是说,我们在一个对象里面去定义方法,因为对象不构成作用域,所以这些方法的父作用域都是全局作用域。箭头函数要去寻找this,就只能找到全局作用域中的this——window对象了。
上面说了这么多,总结一下:vue对传入的方法
methods
对象做了处理,在函数被调用前做了this指向的绑定,只有拥有this的普通函数才能被正确的绑定到组件实例上。而箭头函数则会导致this的指向丢失。结语 「为什么react中用箭头函数,vue中用普通函数」这是一个挺很有意思的问题,简单来说,这种差异是由于我们写的react是一个类,而vue是一个对象导致的。
【我发现了华点(vue规定用普通函数定义方法,为什么react又要我用箭头函数!)】在类中定义只有箭头函数才能根据作用域链找到组件实例;在对象中,只有拥有自身this的普通函数才能被修改this指向,被vue处理后绑定到组件实例。
如果觉得这篇文章对你有帮助,不要忘了给我点个赞,你的支持是我最大的动力
关注我的公众号可以第一时间看到最新文章
文章图片
点个在看更好
推荐阅读
- MySQL 多表联合查询有何讲究()
- 『忘了再学』Shell基础|『忘了再学』Shell基础 — 8、管道符介绍
- 一篇带你了解小米和华为的智能家居生态布局
- Java进阶之路|撸了几行骚代码,解放了双手
- Java进阶之路|崩溃,我带的实习生把图片直接存到了服务器上
- Java进阶之路|厉害,我带的实习生仅用四步就整合好SpringSecurity+JWT实现登录认证
- 硬核实测 | 深信服超融合是Oracle数据库的最佳CP,实锤了!
- 厉害!我带的实习生仅用四步就整合好SpringSecurity+JWT实现登录认证!
- 结合场景,快速了解智能运维算法体系核心知识
- windows|Windows 11正式版来了!一文带你免费升级、镜像下载、最低系统要求