JS方法apply、call和bind的实现是怎样?
Admin 2022-10-31 群英技术资讯 490 次浏览
apply、call和bind都是系统提供给我们的内置方法,每个函数都可以使用这三种方法,是因为apply、call和bind都实现在了Function的原型上(Function.prototype),而他们的作用都是给我们函数调用时显式绑定上this。下面先介绍一下它们的基本用法:
apply方法:调用一个具有给定this值的函数,以及以一个数组(或类数组对象)的形式提供的参数。
使用语法:func.apply(thisArg, [argsArray])
thisArg:在func函数调用时绑定的this值;[argsArray]:一个数组或者类数组对象,其中的数组元素将作为单独的参数传给func函数;
使用效果:
function foo(x, y ,z) { console.log(this, x, y, z) } const obj = { name: 'curry', age: 30 } /** * 1.将obj对象绑定给foo函数的this * 2.数组中的1 2 3分别传递给foo函数对应的三个参数 */ foo.apply(obj, [1, 2, 3])
call方法:使用一个指定的 this值和单独给出的一个或多个参数来调用一个函数。
使用语法:func.call(thisArg, arg1, arg2, ...)
thisArg:在func函数调用时绑定的this值;arg1, arg2, ...:指定的参数列表,将作为参数传递给func函数;
使用效果:
function foo(x, y ,z) { console.log(this, x, y, z) } const obj = { name: 'curry', age: 30 } /** * 1.将obj对象绑定给foo函数的this * 2.call剩余参数中的a b c分别传递给foo函数对应的三个参数 */ foo.call(obj, 'a', 'b', 'c')
bind方法:创建一个新的函数,在bind()被调用时,这个新函数的this被指定为bind()的第一个参数,而其余参数将作为新函数的参数,供调用时使用。
使用语法:func.bind(thisArg[, arg1[, arg2[, ...]]])
thisArg:调用func函数时作为this参数传递给目标函数的值;arg1, arg2, ...:当目标函数被调用时,被预置入func函数的参数列表中的参数;
使用效果:
function foo(...args) { console.log(this, ...args) } const obj = { name: 'curry', age: 30 } /** * 1.将obj对象绑定给foo函数的this * 2.bind剩余参数中的1 2 3分别传递给foo函数中参数 * 3.也可在newFoo调用时传入参数,这时bind传递的参数会与newFoo调用时传递的参数进行合并 */ const newFoo = foo.bind(obj, 1, 2, 3) newFoo() newFoo('a', 'b', 'c')
总结:
apply和call主要用于在函数调用时给函数的this绑定对应的值,两者作用类似,主要区别就是除了第一个参数,apply方法接受的是一个参数数组,而call方法接受的是参数列表。
bind也是给函数指定this所绑定的值,不同于apply和call的是,它会返回一个新的函数,新函数中的this指向就是我们所指定的值,且分别传入的参数会进行合并。
为了所有定义的函数能够使用我们自定义的apply、call和bind方法,所以需要将自己实现的方法挂在Function的原型上,这样所有的函数就可以通过原型链找到自定义的这三个方法了。
Function.prototype.myApply = function(thisArg, argArray) { // 1.获取当前需要被执行的函数 // 因为myApply是需要被当前函数进行调用的,根据this的隐式绑定,此处的this就是指向当前需要被执行的函数 const fn = this // 2.对传入的thisArg进行边界判断 if (thisArg === null || thisArg === undefined) { // 当传入的是null或者undefined是,被执行函数的this直接指向全局window thisArg = window } else { // 将传入的thisArg对象化,方便后面在thisArg添加属性 thisArg = Object(thisArg) } // 也可简单写成三元运算符: // thisArg = (thisArg === null || thisArg === undefined) ? window : Object(thisArg) // 3.将获取的fn添加到thisArg对象上 // 这里使用Symbol的原因是避免外部传入的thisArg中的属性与添加fn有冲突 const fnSymbol = Symbol() Object.defineProperty(thisArg, fnSymbol, { enumerable: false, configurable: true, writable: false, value: fn }) // 也可简单写成 // thisArg[fnSymbol] = fn // 4.对argArray进行判断 // 看是否有传入值,没有值传入就默认 [] argArray = argArray || [] // 5.调用获取的fn函数,并将对应传入的数组展开传递过去 const result = thisArg[fnSymbol](...argArray) // 调用完后删除添加的属性 delete thisArg[fnSymbol] // 6.将结果返回 return result }
测试:虽然打印出来的对象中还存在Symbol属性,实际上已经通过delete删除了,这里是对象引用的问题。
function foo(x, y, z) { console.log(this, x, y, z) } foo.myApply({name: 'curry'}, [1, 2, 3])
call方法的实现和apply方法的实现差不多,主要在于后面参数的处理。
Function.prototype.myCall = function(thisArg, ...args) { // 1.获取当前需要被执行的函数 const fn = this // 2.对传入的thisArg进行边界判断 thisArg = (thisArg === null || thisArg === undefined) ? window : Object(thisArg) // 3.将获取的fn添加到thisArg对象上 const fnSymbol = Symbol() thisArg[fnSymbol] = fn // 4.调用获取的fn函数,并将对应传入的args传递过去 const result = thisArg[fnSymbol](...args) // 调用完后删除添加的属性 delete thisArg[fnSymbol] // 5.将结果返回 return result }
测试:
function foo(x, y, z) { console.log(this, x, y, z) } foo.myCall({name: 'curry'}, 1, 2, 3)
bind方法的实现稍微复杂一点,需要考虑到参数合并的问题。
Function.prototype.myBind = function(thisArg, ...argsArray) { // 1.获取当前的目标函数,也就是当前使用myBind方法的函数 const fn = this // 2.对传入的thisArg进行边界判断 thisArg = (thisArg === null || thisArg === undefined) ? window : Object(thisArg) // 3.将获取的fn添加到thisArg对象上 const fnSymbol = Symbol() thisArg[fnSymbol] = fn // 4.定义一个新的函数 function newFn(...args) { // 4.1.合并myBind和newFn传入的参数 const allArgs = [...argsArray, ...args] // 4.2.调用真正需要被调用的函数,并将合并后的参数传递过去 const result = thisArg[fnSymbol](...allArgs) // 4.3.调用完后删除添加的属性 delete thisArg[fnSymbol] // 4.4.将结果返回 return result } // 6.将新函数返回 return newFn }
测试:
function foo(x, y, z) { console.log(this, x, y, z) } const newFoo = foo.myBind({ name: 'curry' }, 1, 2) newFoo(3)
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:mmqy2019@163.com进行举报,并提供相关证据,查实之后,将立刻删除涉嫌侵权内容。
猜你喜欢
这篇文章主要介绍了Echarts 如何实现一张图现切换不同的X轴,通过动图给大家展示效果,实例代码相结合给大家介绍的非常详细,需要的朋友可以参考下
目录ant design vue的form表单取值官方中有以下两种取值方式ant design of vue 学习之表单formv-decorator(表单验证,内置绑定,初始值)数据获取与填充表单实例ant design vue的form表单取值官方中有以下两种取值方式因为不是很熟悉,所以还是查了文档找了一下使用方式
本篇文章主要介绍了jQuery之动画效果,详细的介绍了各种动画特效的用法,有需要的可以了解一下,希望能够给你带来帮助
微信小程序实现上传视频功能 本文实例为大家分享了微信小程序上传视频,供大家参考,具体内容如下 微信开发者工具需要安装ffmpeg环境才能正常使用下面的官方方法. 1.调用官方提供的方法(wx.chooseMedia) choosevideo(){ let that=this console.log("上传视频的方法") wx.chooseMedia({ count: 1, //上传视频的个数 mediaType:['video'], //限制上传的类型 ...
JS如何进行按位取反计算?一些朋友可能不是了解什么是按位取反运算,按位取反运算的时候,计算机会将操作数所对应的二进制表达式的每一个位进行取反计算,取反后所得到的值就是~按位取反的运算结果。那么JavaScript中如何来实现呢?下面我们一起来看看。
成为群英会员,开启智能安全云计算之旅
立即注册Copyright © QY Network Company Ltd. All Rights Reserved. 2003-2020 群英 版权所有
增值电信经营许可证 : B1.B2-20140078 粤ICP备09006778号 域名注册商资质 粤 D3.1-20240008