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

面试这道题——不推翻

时间:2023-03-27 15:14:29 JavaScript

①this指针的指南首先,要想搞懂指向问题,首先要知道this指针是什么,是什么意思。这个指针是什么?可以认为这是当前函数执行环境的上下文。它是一个指针变量,我们把它理解为一个动态对象,这个动态对象就是当前运行环境的上下文。这个指针是什么意思?重用!这个的出现是为了提高函数的复用性。调用函数时可以使用不同的上下文:用不同的this调用同一个函数会产生不同的结果,就像面向对象编程中多态的思想一样。那么问题来了,既然this指针是一个动态变量,那么我们在调用函数的时候应该用什么规则来确定它的指向呢?this指针的调用规则普通函数(箭头函数另行说明)在调用时遵循以下规则:默认绑定隐式绑定显式绑定新绑定,它们之间的优先级为:新绑定>显示绑定>隐式绑定>默认绑定①默认绑定:函数独立调用,函数引用没有任何修饰先来看一道经典的面试题。varname='Jack'varobj={name:'Tom',foo:function(){console.log(this.name)}}varbar=obj.foobar()在浏览器严格模式下会输出:UncaughtTypeErrorwill浏览器非严格模式下输出:Jack独立调用该函数时,默认绑定生效。本例中函数是在全局环境中调用的,所以this会指向全局对象,而严格模式不允许this指向全局对象。在默认绑定中,有一点需要注意。当一个函数作为参数传递时,比如setTimeout,setInterval,在非严格模式下this仍然指向全局对象。varname='Jack'varobj={name:'Tom',foo:function(){setTimeout(function(){console.log(this.name)},1000)}}obj.foo()仍然输出:Jack②隐式绑定:当函数被调用时,它被修改。比如调用下面的函数时,由对象发起,this指向对象。varname='Jack'varobj={name:'Tom',foo:function(){console.log(this.name)}}obj.foo()遇到隐式绑定的链式调用时,遵循就近原则.这个原则是指:离被调用函数最近的对象就是this的指向。看一个例子:varname='Jack'varperson1={name:'Tom',foo:function(){console.log(this.name)}}varperson2={name:'Harry',friend:person1,}person2.friend.foo()根据就近原则,我们可以轻松推出输出Tom。因为在这个例子中,调用foo()的是person2.friend,那么this指向person1,打印出来的名字就是person1的名字。③显式绑定:通过bind/apply/call改变指向所有函数。可以使用bind/apply/call,因为这三个方法挂载在Function原型下。call和apply都在改变this点后立即执行。它们的区别在于接收的参数类型:call函数接收的是参数列表,apply函数接收的是参数数组。func.call(this,arg1,arg2,...)func.apply(this,[arg1,arg2,...])bind接收参数列表,改变this点后,返回一个新函数,将现在就做。(在非严格模式下,如果不传第一个参数this,默认绑定全局对象。)varperson={name:'Jack'}functionaddInfo(age,work){this.age=age;this.work=work}addInfo.apply(person,['25','HR'])addInfo.call(person,'28','frontend')console.log(person.age)console.log(person.work)如果我们在调用显式绑定方法时传入的this是数字或者字符串,那么This方法会将其转换为对象。functiongetType(){console.log(this,typeofthis)}getType.apply('hi')getType.apply(123)output:[String:'hi']object[Number:123]object④newbindsnew做了什么具体做法是:创建一个空对象,将空对象的原型指向原对象的原型执行构造函数中的方法返回这个新对象functionstudy(name){this.name=name;}varobj=newstudy('Jack')console.log(obj.name)按照newbinding>explicitbinding>implicitbinding>defaultbinding的规则,大家试试看下个例子会返回什么?functionfoo(){console.log(this.a)}varobj1={a:1,foo:foo}varobj2={a:2,foo:foo}obj1.foo();obj2.foo();obj1.foo.call(obj2);obj2.foo.call(obj1);根据显式绑定>隐式绑定的规则,输出:1221Nextdifficultyupgradefunctionfoo(something){this.a=something}varobj1={foo:foo}varobj2={}obj1.foo(2);控制台日志(obj1.a);obj1.foo.call(obj2,3);控制台日志(obj2.a);varbar=newobj1.foo(4);控制台日志(obj1.a);控制台日志(bar.a);输出2324。首先,第一个是输出obj1.a的值,显然obj1.foo(2)是隐式调用,this指向obj1,所以obj1中a的值为2。obj2.a的值是通过显式绑定方式赋值的,显式优先级大于隐式优先级,所以obj2.a的值为3。newobj1.foo(4)时,本质是创建一个新的对象,并返回这个新的对象,所以对obj1本身没有影响。obj1.a的值还是2。bar的值是new出来的新对象,a的值是4。我们再练习一个:functionfoo(something){  this.a=something}varobj1={}varbar=foo.bind(obj1);酒吧(2);控制台日志(obj1.a);varbaz=newbar(3);控制台日志(obj1.a);控制台日志(baz.a);Output:223⑤箭头函数上面提到的四种绑定方式都是针对普通函数的,ES6引入的箭头函数不受前面的规则约束,比较特殊,需要单独分析。普通函数的this点在调用的时候就确定了,而箭头函数的point在定义的时候就确定了。它具有以下特点:箭头函数没有参数箭头函数没有构造函数箭头函数没有原型对象箭头函数没有thisvarname='123';varobj={name:'456',print:function(){functiona(){console.log(this.name);}A();}}obj.print();//输入123functionFoo(){Foo.a=function(){console.log(1);}this.a=function(){console.log(2)}}Foo.prototype.a=function(){console.log(3);}Foo.a=function(){console.log(4);}Foo.a();//4letobj=newFoo();obj.a();//2Foo.a();//1varlength=10;functionfn(){console.log(this.length);}varobj={length:5,method:function(fn){fn();//10个参数[0]();//2}};obj.method(fn,1);