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

深入理解JavaScript——call、apply、bind,三位大将

时间:2023-03-27 23:45:49 HTML

之前讲this关键字的时候介绍了这三个API,得出的结论是call、apply、bind都有“弯曲”this指向的能力当时主要集中在这上面,并没有详细介绍这三个API。在这篇文章中,我们来看看函数中所谓的几个原型方法——call、apply、bindCallMDN:bar={name:'johnny'}functionfoo(){console.log(this.name)}foo.call(bar);//johnny的第一次调用是原型方法,所以一个函数自然可以使用这个方法,即foo的call方法继承自Function.prototype.call。其次,call方法中传入的值,以后会成为foo函数的this指向的对象。其实质就是改变this的point,让this指向(ofcall)传入的值,实现call。我们按照前面的描述来实现callFunction.prototype。mycall=function(context=window,...args){if(this===Function.prototype){returnundefined//防止Function.prototype.mycall直接调用}constfn=Symbol()context[fn]=这;constresult=context[fn](...args)deletecontext[fn]returnresult}我们一步步来分析,首先不支持直接调用Function.prototype.mycall,Function.prototype.call(bar)//undefinedthis很容易理解,Function.prototype中没有this,调用XXXletcontext=context||window,因为需要考虑传入的context是否为null。foo.call(null);如果为null,上下文会指向window,这个很好理解,防御代码constfn=Symbol()context[fn]=this;constresult=context[fn](...args)在传入的上下文中设置一个属性并将其赋值给它,然后执行它。这是调用函数的关键。上面我们讲调用例子的时候说过调用会改变this的方向,this指向传入的上下文。所以,我们在mode中实现调用的时候,可以先使用thiscontext上有一个属性,然后再去执行,this的规则就是谁调用它,它指向谁。这样this指向contextdeletecontext[fn]returnresult删除context属性,释放内存,返回result值。resultcall的实现是这样的。Letbar={name:'johnny'}functionfoo(){console.log(this.name)}foo.mycall(bar);applyMDN:apply()方法使用给定的this值和提供的参数调用函数作为数组(或类似数组的对象)用法:letbar={name:'johnny'}functionfoo(age,hobby){console.log(this.name,age,hobby)}foo.apply(bar,[28,'睡觉']);//johnny28sleep//call如何使用//foo.call(bar,28,'sleep');apply和call在使用上类似,只是传递参数的方式不同foo.call(obj,param1,param2,...,paramN)//传入一串参数foo.apply(obj,[param1,param2,...,paramN])//第二个参数是类数组,所以实现和call差不多。实施applyFunction.prototype.myapply=function(context=window,args){if(this===Function.prototype){returnundefined}constfn=Symbol()context[fn]=thisletresult;if(!Array.isArray(args)){result=context[fn]()}else{result=context[fn](...args)}deletecontext[fn]returnresult}测试我的一波applyletbar={name:'johnny'}functionfoo(age,hobby){console.log(this.name,age,hobby)}foo.myapply(bar,[28,'sleep']);//johnny28sleepbindMDN:bind()方法创建一个新函数。调用bind()时,将这个新函数的this指定为bind()的第一个参数,其余参数将作为新函数的参数调用使用方法:letbar={name:'johnny'}functionfoo(age){console.log(this.name,age)}//返回一个函数letbindBar=foo.bind(bar)bindBar(28)//johnny28和calland一样apply,是函数的原型方法,但是和它们不同的是,它是ES5新增的方法,它返回的是一个函数(而且还支持传参),看到returnisAfunction表示bind方法是闭包实现bindFunction.prototype.mybind=function(context,...args1){if(this===Function.prototype){thrownewTypeError('Error')}const_this=thisreturnfunctionF(...args2){if(thisinstanceofF){returnnew_this(...args1,...args2)}return_this.apply(context,args1.concat(args2))}}我们来分析下如何实现bind。首先,bind不能调用原型方法。如果使用,会提示报错。其次,我们根据bind的一个特点,把它的使用分为两种。一个绑定函数也可以使用new运算符来创建对象:这种行为就像使用原始函数作为构造函数一样提供的this值被忽略,调用时的参数提供给模拟函数。也就是说,我们需要判断是否是构造函数调用,如果是,则使用new调用当前函数;if(thisinstanceofF){//如果是构造函数,则调用以foo(即_this)为构造函数的函数returnnew_this(...args1,...args2)}//如果不是构造函数,使用apply指向代码return_this.apply(context,args1.concat(args2))testawave//测试非构造函数使用letbar={name:'johnny'}functionfoo(age,hobby){console.log(this.name,age,hobby)}//返回一个函数letbindBar=foo.mybind(bar,28)bindBar('sleep')//johnny28sleep//在测试构造函数时使用函数Person(name,age){this.name=name;this.age=age;}Person.prototype.sayName=function(){console.log('mynameis'+this.name)}letemptyObj={}varFakerPerson=Person.mybind(emptyObj)varjohnny=newFakerPerson('johnny',28)johnny.sayName()//mynameisjohnny总结call,apply,bind可以修改这点,wherecall,apply定义在ECMAScript3中,它们满足了大部分开发者的需求,即,改变这个方向。原理是将this赋值给context的一个属性(传入的值)并执行(谁调用this,this指向谁)两者的区别在于调用方式不同,call开始传入一个来自第二个参数appl的一系列参数y的第二个参数是一个数组,它接受所有参数。它们也是一种实现继承的方式——ECMAScript5中出现了借用构造函数和bind,是对修改this指针的补充。它以闭包的形式存在,它具有三个特点。它返回一个可以传参的函数(bind生成的函数和bind生成的函数都可以传参)。输入的参数仍然有效。导数思维题:array和array-like有什么区别?Array-like是一个具有长度和索引属性的对象。Array-like是一个普通的对象,但是真正的数组是Array类型的,所以array-like的原型关系和array是不一样的。常见的类数组有:arguments,DOMListofobjects(document.querySelectorAll)将类数组数组转换为数组Array.prototype.slice.call(arrayLike,0)[...arrayLike](spreadoperator)数组。from(arrayLike)参考资料JavaScript深入调用与应用JavaScript的模拟实现深入bind模拟实现系列文章深入理解JavaScript-深入理解JavaScript的开始-什么是JavaScript深入理解JavaScriptJavaScript——JavaScript由什么组成深入理解JavaScript——万物皆对象)深入理解JavaScript——new是做什么的?深入理解JavaScript——Object.create深入理解JavaScript——复制JavaScript的秘诀——instanceof——找祖宗深入理解JavaScript——Function深入理解JavaScript——Scope深入理解JavaScript——this关键字深入理解JavaScript——call,apply,bindIIFE)深入理解JavaScript——词法环境深入理解JavaScript——执行上下文与调用栈深入理解JavaScript——ScopeVSExecutioncontext深入理解JavaScript——闭包深入理解JavaScript——防抖与节流深入理解JavaScript——函数式编程深入理解JavaScript——垃圾回收机制深入理解JavaScript——Array深入理解JavaScript——Loopscomehere深入理解JavaScript——String