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

吃透js中this的方向(包括js绑定,优先级,面试题详解)

时间:2023-03-26 23:07:22 JavaScript

为什么要用这个在javascript中,这个是无处不在的。它可以用来指向某些元素和对象。在适当的地方使用它可以减少无用代码的编写.log(user.name+'dancing')},study:function(){console.log(user.name+'Learning')},}上面代码中,每个方法都需要用到user中的name属性目的。如果用户对象的名称发生变化,则必须更改所有方法。在这种情况下,使用this是一个不错的选择varuser={name:"aclie",sing:function(){console.log(this.name+'singing')},dance:function(){console.log(this.name+'dancing')},study:function(){console.log(this.name+'studying')},}this指向this与函数定义位置无关,如何调用下面的foo函数调用方式不一样,this的值也不一样hello")执行结果如下图所示。this的四种绑定方式1.默认绑定函数独立调用时,this默认绑定到window//1.直接调用functionfoo(){console.log(this)}foo()//2.函数在对象中varobj1={foo:foo}varfn1=obj1.foofn1()//3.被全局变量引用varobj2={bar:function(){console.log(this)}}varfn2=obj2.barfn2()//4.函数嵌套调用functionfoo1(){console.log('foo1',this)}functionfoo2(){console.log('foo2',this)foo1()}functionfoo3(){console.log('foo3',this)foo2()}foo3()//5.调用varobj2={bar:function(){returnfunction(){console.log(this)}}}obj2.bar()()执行结果如下以上5种调用方式都属于默认绑定,因为最后都是分别调用函数。2.隐式绑定调用的对象引用了函数functionfoo(){console.log(this)}varobj1={name:'obj1',foo:foo}obj1.foo()varobj2={name:'obj2',bar:function(){console.log(this)}}obj2.bar()varobj3={name:'obj3',baz:obj2.bar}obj3.baz()的执行结果上面的代码就是以上三种都是隐式绑定,都是通过对象调用,this指向对象3.显式绑定我不想在对象内部包含这个函数的引用,但是我想通过对象强制调用,使用call/apply/bind进行显式绑定functionfoo(){console.log(this)}varobj={name:'obj1',}foo.call(obj)foo.apply(obj)foo.call("xxx")上面代码的执行结果是foo函数直接调用this应该指向window,这里通过call/apply改变this的指向4.newbinding通过构造函数创建实例new关键字,并绑定这个函数Person(name,age){this.name=namethis.age=age}constp1=newPerson('爱丽丝',20)constp2=newPerson('mogan',24)console.log(p1)console.log(p2)上面代码的执行结果如下,此时this指向创建的实例对象new和boundtothis优先级1.隐式绑定高于默认绑定functionfoo(){console.log(this)}varobj={name:'obj',foo:foo}obj.foo()的执行结果上面的代码为foo函数默认绑定了window对象。当隐式绑定和默认绑定同时存在时,隐式绑定的优先级高于默认绑定。2.显示绑定高于隐式绑定。//Case1varuser={name:'user',foo:function(){console.log(this)}}user.foo.call('kiki')//Case2functionfoo(){console.log(this)}varobj={name:"obj",foo:foo.bind("aclie")}obj.foo()上面代码的执行结果是,如果隐式绑定优先级更高,this应该都指向对象,但是根据上面的执行结果可以看出这个绑定是显式绑定的结果,所以当同时存在隐式绑定和显式绑定时,显式绑定的优先级高于隐式绑定。3.new高于隐式绑定varuser={name:'lisa',foo:function(){console.log(this)}}newuser.foo()上面代码执行结果如下new关键字绑定和隐式绑定都存在,this绑定到foo构造函数,所以new关键字的优先级高于隐式绑定4.new高于显式绑定functionbar(){console.log(this)}varfn=bar.bind('hello')newfn()上面代码的执行结果如下当new关键字绑定和display绑定都存在时,this绑定到bar构造函数,所以优先新关键字高于显示绑定的关键字,以上四种绑定的优先级顺序为new关键字>显式绑定>隐式绑定>默认绑定规则之外还有几种特殊的绑定方式,不包含在以上四种绑定规则中,忽略显示绑定当显示绑定值为null/undefined时,this直接绑定windowvaruser={name:'alice',foo:function(){console.log(this)}}user.foo()user.foo.call(null)user.foo.apply(undefined)上面代码的执行结果如下2.间接函数引用varobj1={name:'obj1',foo:function(){console.log(this)}}varobj2={name:'obj2'};obj2.baz=obj1.foo;obj2.baz();(obj2.bar=obj1.foo)()上面代码的执行结果是两个方法绑定的this是不同的。第二种方式是调用赋值,其实就是一种间接的函数引用。(obj2.bar=obj1.foo)这里返回赋值的结果,加上括号直接调用赋值的结果函数。3.箭头函数箭头函数不绑定this,它的this来自上级作用域varuser={name:'kiki',foo:()=>{console.log('箭头函数中的this',this)}}上面代码user.foo()的执行结果如下在这里调用foo函数,因为箭头函数没有绑定this,所以到foo函数的上层去找this,找到globalobjectwindow面试题1,考察间接函数引用varname="window";varperson={name:"person",sayName:function(){console.log(this.name);}};functionsayName(){varsss=person.sayName;sss();person.sayName();(person.sayName)();(b=person.sayName)();}sayName();执行sayName函数变量sss由person.sayName方法赋值,执行sss函数,这是一个独立的函数调用,this指向全局窗口,all游戏中的变量name绑定了window,所以this.name就是"window"person.sayName()是隐式绑定,this指向person对象,所以this.name就是person.name,即"person"(person.sayName)()本质上和前面一样,隐式绑定,this指向person对象,所以this.name就是person.name,即"person"(b=person.sayName)()是间接函数引用,person.sayName赋值给b变量,括号代表赋值结果,this指向window,this.name为window.name,即“window”,所以执行结果为2,定义对象时不生成scopevarname='window'varperson1={name:'person1',foo1:function(){console.log(this.name)},foo2:()=>console.log(this.name),foo3:function(){returnfunction(){console.log(this.name)}},foo4:function(){return()=>{console.log(this.name)}}}varperson2={name:'person2'}person1.foo1();person1.foo1.call(person2);person1.foo2();person1.foo2.call(person2);person1.foo3()();person1.foo3.call(person2)();person1.foo3().call(person2);person1.foo4()();person1.foo4.call(person2)();person1.foo4().call(person2);调用流程分析foo1函数person1.foo1()隐式绑定,this指向person1,this.name为person1.name,即“person1”person1.foo1.call(person2)隐式绑定+显式绑定person2,显示绑定优先级较高,所以this指向person2,this.name为person2.name,即“person2”foo2函数person1.foo2()是隐式绑定的,箭头函数没有自己的this,所以向上层查找作用域,找到全局窗口(person1是一个对象,当itisdefined),全局变量名为Bindingtowindow,this.name为window.name,即“window”person1.foo2.call(person)隐式绑定+显式绑定,但箭头函数不绑定this,这里的显式绑定是无效的。如果您没有自己的这个,请查找上层范围并找到全局窗口。this.name为window.name,即“window”foo3functionperson1.foo3()()这个相当于执行了person1.foo()的返回函数,这里是一个独立的函数调用,this指向全局窗口,this.name为window.name,即“window”person1.foo3.call(person2)()这里通过调用改变了foo3函数中this的指向,但是最后执行的是闭包foo3函数返回,闭包作为一个独立的函数被调用,this仍然指向全局窗口,this.name为window.name,即'window'person1.foo3().call(person2),这里,foo3函数返回的闭包说明绑定了person2对象,this指向person2,this.name为person2.name,即"person2"foo4函数person1.foo4()()执行person1的返回值.foo(),返回的闭包是一个箭头函数,没有。为此,向上查找作用域,找到foo4函数foo4的this指向person1,所以闭包的this也指向person1,而thiss.name就是person1.name,即"person1"person1.foo4.call(person2)()返回的闭包没有this,和foo4函数在上层作用域中找到,foo4函数的this显示为绑定变成person2,所以闭包的this也指向person2,this.name就是person2.name,即“person2”。person1.foo4().call(person)返回的闭包是箭头函数,不能通过call显示绑定,直接向上层作用域搜索,找到foo4函数。foo4的this指向person1,所以闭包的this指向person1,this.name就是person1.name,即“person1”。上面代码的执行结果如下3.构造函数中定义了函数,函数的上级作用域是构造函数varname='window'functionPerson(name){this.name=namethis.foo1=function(){console.log(this.name)},this.foo2=()=>console.log(this.name),this.foo3=function(){returnfunction(){console.log(this.name)}},this.foo4=function(){return()=>{console.log(this.name)}}}varperson1=newPerson('person1')varperson2=newPerson('person2')person1.foo1()person1.foo1.call(person2)person1。foo2()person1.foo2.call(person2)person1.foo3()()person1.foo3.call(person2)()person1.foo3().call(person2)person1.foo4()()person1.foo4.call(person2)()person1.foo4().call(person2)调用分析过程foo1函数person1.foo1()是隐式绑定的,this指向person1,person1创建实例时传入的名字是person1,所以这个.name是person1person1.foo1.call(person2)隐式绑定+显式绑定,显示绑定优先级高,绑定person2,person2创建实例时传入的name是person2,所以this.name就是person2foo2函数person1.foo2()是隐式绑定的,但是foo2是一个箭头函数,没有它自己的this。搜索上层作用域并找到Person构造函数。此时this指向对象person1,在实例化person1的时候传入的名字是person1,所以this.name是person1person1.foo2.call(person2)隐式绑定+显式绑定,但是foo2是箭头函数和没有绑定this,所以还需要在上层scope中查找this,找到Person构造函数,this指向person1对象,所以this.name为person1foo3functionperson1.foo3()()执行person1.foo3的返回值,返回函数被独立调用,this指向window,全局name变量绑定在window中,this.name为window.name,即“window”person1.foo3.call(person2)()的显式binding改变了foo3函数的this,最终执行的是foo3函数的返回值,仍然是函数独立调用,所以this指向window,this.name为window.name,即“window”person1.foo3().call(person2)foo3函数的返回函数将this绑定到person2通过显式绑定,和person2创建实例时传入的名字是person2,所以this.name是person2foo4函数person1.foo4()()执行foo4函数的返回值。返回函数是没有this的箭头函数,所以搜索上层作用域发现foo4函数this指向person1,所以箭头函数的this也指向person1,所以this.name为person1person1.foo4.call(person2)()foo4通过显式绑定将this绑定到person2,返回的函数是箭头函数,this和foo4的父作用域是一致的,所以箭头函数的this也指向pperson2,所以this.name是person2person1.foo4().call(person2)foo4函数的返回值是一个没有绑定this的箭头函数,说明绑定无效。在上层作用域中搜索this,找到foo4函数,this指向person1的执行结果如下4.区分作用域varname='window'functionPerson(name){this.name=namethis.obj={name:'obj',foo1:function(){返回函数(){console.log(this.name)}},foo2:function(){return()=>{console.log(this.name)}}}}varperson1=newPerson('person1')varperson2=newPerson('person2')person1.obj.foo1()()person1.obj.foo1.call(person2)()person1.obj.foo1().call(person2)person1.obj.foo2()()person1.obj.foo2.call(person2)()person1.obj.foo2().call(person2)foo1functionperson1.obj.foo1()()执行foo1函数的返回函数,这个函数此时是一个独立的函数调用,这个指向window,全局变量name加入window,这里this.name指向window.name,即“window”person1.obj.foo1.call(person2)()这说明绑定改变了方向foo1中的this,但是最后执行的是foo1函数的返回值,返回函数作为一个独立的函数被调用,this仍然指向window,所以this.name就是window.name,即“window”person1。obj.foo1().call(person2)这里是通过displaybindingfoo1改变的this在number返回函数中的指向,所以这个函数指向了person2,而person2在实例化的时候传入了person2的name值,所以this.name就是person2foo2函数person1.obj.foo2()()执行返回的foo2函数,此时该函数是一个独立的函数调用,但是它没有this,所以需要查找上层作用域,发现foo2函数的this指向obj,所以该函数的thisfunction也指向obj,this.name为obj.name,即“obj”person1.obj.foo2.call(person2)()执行foo2的返回函数。此时函数作为一个独立的函数被调用,但它本身并没有this。需要在上层范围内搜索。foo2函数的this被display绑定,必须变成person2,所以这个函数也是person2,person2在实例化的时候传入了person2的name值,所以this.name就是person2person1.obj.foo2().call(person2)foo2的返回函数是箭头函数没有绑定this,显式绑定无效,它没有自己的this。需要查找上层作用域,发现foo2函数的this指向obj,所以函数的this也指向obj,this.name为obj。name,也就是“obj”,所以执行结果就是上面这个指向的理解。关于高级js,开发者需要掌握的东西还是很多的。可以看看我写的其他博文,持续更新中~