当前位置: 首页 > Web前端 > JavaScript

吃透js中this的指向问题

时间:2023-03-27 12:32:31 JavaScript

在写js、react等代码的时候,经常会遇到使用this的情况。当我满怀信心写完代码点击运行时,发现报错this.xxxisundefined或者this.xxxisnotafunction,butxxxisclearlydefinedinyourcodeinyourcode。为什么会出现这些错误?其实就是this指向的对象中没有定义xxx。所以了解这个方向是非常有必要的。1.执行上下文在开始讲这个之前,先简单说一下执行上下文。执行上下文分为三部分:变量对象(VariableObject,VO)作用域链this,这里的this就是本文要讲的this。执行上下文分为:全局执行上下文函数执行上下文全局执行上下文在代码执行时创建,函数执行上下文在函数调用时创建。同一个函数,在不同的时间调用,会创建不同的执行上下文。2.this的指向由于this存在于执行上下文中,而执行上下文是在代码执行和函数调用时创建的,因此this的指向也是在代码执行和函数调用时确定的。this可以在不同的场景下使用,this的指向分为三种情况:this在全局this函数中,this在构造函数中,this在全局this在浏览器中,this在全局指向window中,节点。js中的globalthis指向地球。变量a=1;控制台日志(这个);//windowconsole.log(this.a);//1console.log(window.a);//1console.log(this===window);//true函数中的this按函数调用方式分为以下两种情况:this当函数作为对象中的方法调用时,this1在声明函数中,this在对象方法中当函数被独立调用当函数作为对象中的方法被调用时,函数中的this指向函数的所有者。变量a=111;varobj={a:222,func:function(){console.log(this);//objconsole.log(this.a);//222}}对象函数();at这里调用了func函数作为obj对象中的一个方法,所以func中的this指向了func函数的拥有者,也就是这里的obj2。独立调用函数时,函数中的this指向全局上下文中的this为window;在严格模式下,这是未定义的。vara=111;函数func1(){vara=222;控制台日志(这个);//窗口console.log(this.a);//111}func1();functionfunc2(){“使用严格”console.log(this);//未定义}func2();ES6箭头函数中的this箭头函数中的this点是在函数定义时确定的,这与声明式函数定义不同。定义箭头函数时,this指向的函数,thisvarfunc=()=>{console.log(this);//窗口}函数();func函数是一个箭头函数,所以func函数里面的this在定义的时候就确定了,定义它的执行上下文就是全局执行上下文。上下文中的this指向window,所以func函数的this也指向this。变量a=111;varobj={a:222,func:function(){varfun2=()=>{vara=333;控制台日志(this.a);}fun2();}}对象。功能();这段代码中,调用func函数时,func中的this指向obj,而func函数中又定义了一个箭头函数,所以func2函数中的this指向了func函数this的执行上下文,所以最后的打印输出是obj对象中的a(222)。构造函数new中this构造函数创建对象的过程:创建一个新对象;将构造函数的this指向这个新对象;执行构造函数的内部代码;在构造函数中返回这个新对象指向生成的对象functionObj(a){this.a=a;this.func=function(){console.log(this);控制台日志(this.a);}}varobj=newObj(2);obj.func();这段代码中,使用new实例化对象后,调用对象中的func函数,其中func函数是obj对象中的一个方法,所以func函数中的this指向函数的所有者,所以这里打印了结果是2。3、改变this指向的方法可以在函数vara=111;中使用call,apply,bind改变this指向。varobj1={a:222,func:function(){console.log(this);//{a:333}console.log(this.a);//333}}varobj2={a:333,}obj1.func.call(obj2,);这里在调用obj1中的func方法时,会在后面添加call后,将func中的this改为指向call中指定的obj2,所以最后打印出来的是333,这里调用call,函数将立即执行。apply方法的作用与call相同。它们的区别在于调用是一个一个传递其他参数,而apply传递的是一个数组,这样当传递的数据数量增加时,就不需要Like调用了,在一个一个调用后写在括号里.例如:obj.func.call(obj2,param1,pram2,param3)obj.func.aply(obj2,[param1,pram2,param3])bind也可以改变函数中的this点,它和call的区别而apply是,它返回一个新函数,并不像call和apply那样立即执行。4.其他情况下的this点当函数作为DOM事件处理器使用时当函数作为事件处理器使用时,它的this指向触发事件的元素。函数func(){console.log(this);//元素}varbutton=document.getElementsByTagName('button');button.addEventListener('click',func);当使用addEventListener向DOM元素添加事件时,函数this将指向DOM元素。当用作内联事件处理程序时button触发事件回调此代码将在控制台上打印窗口。所以这里需要注意的是,函数作为内联事件使用时,函数外层的this指向DOM元素,但是调用函数时,属于独立调用的函数,所以函数里面的this指向了window。React中的This使用类来定义React中的组件。render中的this仍然指向实例化的对象(组件),但是当组件中的函数作为onClick回调时,函数中的this指向它的所有者。importReact,{PureComponent}from'react';classAppextendsPureComponent{constructor(props){super(props);this.state={ldate:newDate()}//this.startTick=this.startTick.bind(this);//this.stopTick=this.stopTick.bind(this);//this.tick=this.tick.bind(this);}componentDidMount(){//this.TimeID=setInterval(this.tick,1000);}componentWillUnmount(){clearInterval(this.TimeID);}startTick(){console.log(this);this.TimeID=setInterval(this.tick,1000);}stopTick(){clearInterval(this.TimeID);}tick(){console.log(this);this.setState({ldate:newDate()})}render(){const{ldate:localstate}=this.state;返回(

{localstate.toTimeString()}
点击开始点击结束
)}}exportdefaultApp;这段代码,因为render是App组件里面的一个方法,所以render中的this指向App实例化后的对象(App)。当使用App中的方法作为render中点击事件的回调时,onClick={this.startTick}中的this是指向的app,所以可以获取到startTick函数;当点击事件被触发时,由于React内部处理了JSX代码并绑定了回调函数的this,所以触发startTick时的this是未定义的(这里是作者个人推测);在startTick函数中,this.tick函数作为计时函数的回调。由于this是undefined,找不到tick函数,会报错,解决React中this的指向问题。在构造函数中使用bind绑定组件函数定义中函数的this和component绑定,使用箭头函数定义constructor(props){super(props);this.state={ldate:newDate()}this.startTick=this.startTick.bind(this);this.stopTick=this.stopTick.bind(this);this.tick=this.tick.bind(this);}startTick=()=>{this.TimeID=setInterval(this.tick,1000);}参考MDN-this