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

JavaScript深入理解系列:call和apply

时间:2023-03-28 01:17:35 HTML

定义call和apply:函数call()方法被执行,函数里面的this会指向第一个参数值,除了第一个参数值之后的几个分支都是passedintothefunction,简而言之就是在函数运行的时候改变this点。使用示例:fn.call(obj,args1,args2...),fn.apply(obj,[arg1,arg2...]),call和apply除了第二个参数传递方式不同外其他都是一样的。例如1:varobj={name:'Programmer'};functionfn(){console.log(this.name)}fn()//this.name=>undefinedfn.call(obj)//this.name=>'程序员米粉'总结:1、fn函数调用call方法执行时,函数的this指向obj2,执行call方法和fn函数。如果你还不明白,当执行fn.call(obj)时,可以认为是在obj对象中执行了一个fn函数。那么fn函数中的this点就是obj又如2:varobj={name:'程序员米粉',fn:function(){console.log(this.name)}};obj.fn();//this.name=>'程序员米粉'总结:1.当执行例子1中的fn.call(obj)时,相当于例子2的obj.fn();当call方法被调用时,会改变当前调用函数的执行上下文(也就是改变this的指向)。SimulationStep1Example1:varobj={name:'ProgrammerRice'};functionfn(){console.log(this.name)}fn.call(obj)//this.name=>'ProgrammerRice'例子2:varobj={name:'程序员米粉',fn:function(){console.log(this.name)}};obj.fn();//this.name=>'程序员米粉'实现自己的myCall方法,首先观察例1和例2中测得的obj对象的区别,区别:1、例2中obj增加了fn函数方法。2、例2中的执行方式为obj.fn()执行,而例1中的执行方式为fn.call(obj)。这个好办,我们怎么把例1改成例2呢?下面总结一下步骤:1.将fn函数设置为对象的一个??属性2.调用fn函数执行。3.调用完成后,从对象中删除fn属性(函数)。(给对象添加属性,我们调用后删除即可)有了思路,我们可以这样写代码:obj.fn=fn;obj.fn();deleteobj.fn;//按照上面的思路删除属性,然后按照这个思路写自己的myCall方法:Function.prototype.myCall=function(context){context.fn=this;上下文.fn();deletecontext.fn;};varobj={name:'程序员米粉'};functionfn(){console.log(this.name);}fn.myCall(obj)//this.name=>'程序员米粉'````执行myCall方法,可以得出this指向obj对象,并打印期望值,至此该方法第一步完成!#Simulationstep2继续改进myCall方法,更改fn方法,增加2个参数,再次执行该方法。//想想怎么给fn函数传参数执行context.fn(arguments[1],arguments[2]);可能有人会想到下面的方法://通过join方法将数组中的元素传入函数中参数context.fn(args.join(','))//这种情况下执行context.fn(args.join(','))变成:context.fn("arguments[1]","arguments[2]");//变成字符串,变成死值,不管传什么,它将变成一个字符串"arguments[1]","arguments[2]"。...可能有人会想到用ES6的方式来解决。这个调用是ES3的方法,所以还是用ES6的方法解决不了。其实我们可以使用eval方法来解决。查看文档可知eval()函数会将传入的字符串作为JavaScript代码执行。这到底是什么意思?eval()的参数是一个字符串。如果字符串表示一个表达式,则eval()计算该表达式。如果参数表示一个或多个JavaScript语句,则eval()会执行这些语句。例如:console.log(eval('2+2'));//2+2=>4,最终输出为2console.log(eval('context.fn('+args+')'))//相当于运行context.fn(arguments[1],arguments[2],...),使用eval执行一串JavaScript语句。+[eval更详细链接;](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/eval)args会自动调用Array.toString()方法。通过eval自动执行就变成了context.fn(arguments[1],arguments[2])这样执行。代码如下:varobj={name:'程序员米饭'};函数fn(index,value){console.log(this.name);//程序员米饭console.log(index,value);//111i是一个值}Function.prototype.myCall=function(context){context.fn=this;变量参数=[];对于(vari=1;i'程序员米粉'执行方法,输入完全符合我们的预期,我们终于搞定了!#模拟步骤3##第一个参数传null或undefined,this指向window例如:varname='程序员米粉'functionfn(){console.log(this.name);//程序员米粉}fn.call(null);//this.name=>'程序员米粉'fn执行的时候,还是输出this.name='程序员米粉',什么意思,只要第一个参数传null或者undefined,函数调用call方法,this指向window##函数执行call方法,如果有返回值,则返回。例如:varname='程序员米饭'functionfn(){console.log(this.name);//程序员米饭return{name:this.name}}fn.call(null);//this.name=>'程序员米粉'//执行fn函数,直接返回对象//{//name:'程序员米粉'//}##最终版实现调用代码varobj={name:'程序员米粉'};functionfn(index,value){console.log(this.name);//程序员米饭console.log(index,value);//111我是一个值return{name:this.name,index:index,value:value};}Function.prototype.myCall=function(context){varcontext=context||window;context.fn=this;varargs=[];for(vari=1;i'程序员米粉'//最终输出//{//name:"程序员米粉"//index:111//value:"我是一个值"//}#实现applycode由于apply和call的原理基本相同,就不详细描述了,直接添加代码即可:Function.prototype.myApply=function(context,arr){varcontext=context||窗口;context.fn=this;var结果;if(!arr){result=context.fn();}else{varargs=[];对于(vari=0;i