当前位置: 首页 > 科技观察

知道这个,用这个!(总结一下this的常用用法)

时间:2023-03-17 22:55:46 科技观察

这应该是大家讨论很久的话题了。其中,在很多博客中都有很多关于这个的文章。但是,我以前是一知半解。我只是读博客。我只是介绍了这个在某些情况下的用法,但我自己并不知道怎么用。做一个总结。刚在掘金看到一些关于这方面的详细文章,文末附上链接和英文原文。这纯粹是给自己的一个总结,方便以后复习加深印象。希望这篇文章能帮助你理解这一点,文末还有一些习题~希望对你真的有帮助。(因为在写项目的过程中,一直被这个坑,但是找bug找了半天,真的很开心。)在理解这个之前,相信大家应该知道知识点的存在的范围。函数创建后,会构建自己的执行环境和作用域,这是一开始就确定的。但实际的上下文(context)环境也可以理解为this,它是动态确定的,即this指向的对象是函数运行时确定的,而不是声明时指向的对象。对此,归纳起来,主要有以下几种方式可以使用。1在对象方法中调用this如果函数被对象的某个方法调用,则this值指向该对象。varperson={name:'Alice',sayName:function(){alert('welcome'+this.name);}}person.sayName();//this==person,alert:'welcomeAlice'这里,函数this指向对象(即人);但是需要注意的一点是,当对象的方法被赋值给一个变量时,它就变成了一个函数触发器。此时this为window或undefined(严格模式下),如下:varname='Bob';varperson;//就是上面的定义,这里不展开细说,直接用varsay=person.sayName;//this==window||undefinedsay();//'welcomeBob'||throwanerror:Cannotreadproperty'name'ofundefined(...)2函数内部使用this在函数内部使用,即函数内部使用asamethod,与1中作为object使用的方法不同,此时调用是在全局作用域下调用,也就是在window下调用,从定义中我们可以知道是声明了一个函数在全局范围内,它会自动添加为窗口的属性。这时候就名正言顺地指向window了。在严格模式下,就是undefinedfunctionsayThis(){alert(this==window);//true}结合第一点,函数作为对象的一个??方法。这里有一个小坑,就是closePackage,什么是闭包,这里就不说了。最简单的理解就是返回函数的Function。如果你不明白什么是闭包,可以阅读第7章关于闭包的《JavaScript 高级程序设计》内容。第一点有个小坑,就是当对象的方法赋值给变量时,就变成了函数触发器。这时候this其实指向了window(非严格模式)。那么,当函数返回一个函数时,此时调用对象中的方法就相当于一个函数触发器。此时this,没有任何上下文绑定,指向window(非严格模式)。varname='Bob',person={name:'Alice',sayName:function(){console.log(this===person);//truereturnfunction(){console.log(this===person);//falseconsole.log(this===window);//trueconsole.log(this.name);//Bob};}};person.sayName()();当然这个问题的解决方法很简单,就是给他绑定一个context。varname='Bob',person={name:'Alice',sayName:function(){console.log(this===person);//truereturnfunction(){console.log(this===person);//trueconsole.log(this===window);//falseconsole.log(this.name);//Alice}.bind(this);}};person.sayName()();知道在使用new方法创建对象时,会经历以下过程:创建一个对象,将this赋值给新对象调用构造函数,给this添加属性和方法returnthis给当前对象functionPerson(name,age){this.name=name;this.age=age;}varperson1=newPerson('Alice',29);console.log(person1.name);//这里Alice一定要记得用new运算符,否则,只能算是普通调用,不能创建新的实例对象。而作为一个普通的函数调用,其实在第二种情况下,对函数的正常调用,此时this指向windowfunctionPerson(name,age){this.name=name;this.age=age;returnthis;}varperson1=Person('Alice',29);console.log(person1.name);//Aliceconsole.log(window.name);//Aliceconsole.log(person1===window);//true这是正常的,this会正确返回并指向对象,但是在构造函数中,如果返回了一个对象,那么this会指向返回的对象。functionPerson(name,age){this.name=name;this.age=age;return{name:'Bob'};}varperson1=newPerson('Alice');console.log(person1.name);//bobconsole.log(person1.age);//undefined题外话,同样的,想想vara=newPerson(),一个instanceofPerson是不是一定返回true?我留给你考虑。4使用call、apply或bind改变this在引用类型Function中,function有两个方法属性,call和apply。在ECMAScript5中,添加了bind方法。顺便说一句,您应该知道它们三个之间的区别。不知道的就赶快学习吧。varname='Bob';varperson={name:'Alice',age:29}functionsayName(){console.log(this.name);}sayName.call(person);//这里的Alice使用call方法来这个的执行环境改变了。至于使用apply,效果是一样的,只是两者的区别在于传入参数的不同。func.call(context,arg1,arg2,...)func.apply(context,[arg1,arg2,...])使用bind方法来改变上下文。bind方法与call和apply有根本的不同。不同的是,bind()函数返回的是一个新的函数,即一个方法,而后两者是立即执行的函数,使用时调用,并返回方法运行的结果。而且,使用bind()方法创建的上下文是永久上下文,不能修改,即使使用call或apply方法,this指向的值也不能修改。varname='Bob';varperson={name:'Alice',age:29}functionsayName(){console.log(this.name);}varsay=sayName.bind(person);say();//AlicesayName();//Bob5箭头函数箭头函数不创建自己的上下文,它的上下文this依赖于定义时的外部函数。而且,箭头函数有一个静态的上下文,即一旦绑定,就不能再修改,即使使用第四种用法中改变上下文的方法,也不会被移动。varnum=[1,2,3];(function(){varshowNumber=()=>{console.log(this===num);//trueconsole.log(this);//[1,2,3]}console.log(this===num);//trueshowNumber();//true&&[1,2,3]showNumber.call([1,2]);//true&&[1,2,3]showNumber.apply([1,2]);//true&&[1,2,3]showNumber.bind([1,2])();//true&&[1,2,3]}).call(num);由于箭头函数的外部决策上下文和静态上下文的特点,不建议使用箭头函数在全局环境中定义方法,因为上下文不能被其他方法改变。这很糟糕。functionPeriod(hours,minutes){this.hours=hours;this.minutes=minutes;}Period.prototype.format=()=>{console.log(this===window);//=>true返回这个。hours+'hoursand'+this.minutes+'minutes';};varwalkPeriod=newPeriod(2,30);console.log(walkPeriod.hours);walkPeriod.format();//=>'undefinedhoursandundefinedminutes'这个其实是它指向的到窗口,所以this.hours和this.minutes实际上没有声明,所以它们是未定义的。在全局环境中,仍然使用函数表达式来定义函数,可以保证正确的上下文{console.log(this===walkPeriod);//=>truereturnthis.hours+'hoursand'+this.minutes+'minutes';};varwalkPeriod=newPeriod(2,30);walkPeriod.format();//'2hoursand30minutes'exercise//Exercise1varfunc=(function(a){this.a=a;returnfunction(a){a+=this.a;returna;}})(function(a,b){returna;}(1,2))func(4)//?//练习2varx=10,foo={x:20,bar:function(){varx=30;returnthis.x;}}console.log(foo.bar());console.log((foo.bar)());console.log((foo.bar=foo.bar)());console.log((foo.bar,foo.bar)());希望看完这篇文章,你可以做这两个练习~下面我们来仔细分析一下。不清楚的可以在留言板上互相讨论。