深入理解JavaScript系列(8):call/apply
call bind apply 的区别
首先,我们先来了解一下 call
,apply
,bind
的区别,方便我们去手写实现自己的 call、apply、bind
。
区别 | bind | call | apply |
---|---|---|---|
是否立即调用 | 否 | 是 | 是 |
参数一 | 指定的对象(该函数的执行上下文 | 指定的对象(该函数的执行上下文 | 指定的对象(该函数的执行上下文 |
其他参数 | 后面的参数都是传入函数的值 | 后面的参数都是传入函数的值 | 只有两个参数,第二个参数是数组 |
call 实现
我们可以分为七个步骤:
- 判断调用对象是否为函数,即使我们是定义在函数的原型上的,但是可能出现使用
call
等方式调用的情况- 判断传入上下文对象是否存在,如果不存在,则设置为
window
- 处理传入的参数,截取第一个参数后的所有参数
- 将函数作为上下文对象的一个属性
- 使用上下文对象来调用这个方法,并保存返回结果
- 删除刚才新增的属性
- 返回结果
第一版
call
在使用一个指定的 this
值和若干个指定的参数值的前提下调用某个参数或方法。
1 | let value = 1; |
我们来看看步骤:
- 第一步就不用多说了,你可以直接使用一个
Object
对象去调用试一下,是抛出了一个错误的,这里我们用打印来表示。 - 第二步就是获取该方法的上下文对象,并调用这个方法了。
- 第三步就是删除新增的属性。
1 | Function.prototype.myCall = function (context) { |
这时候我们会发现打印结果对上了,也是 2
。
第二版
接下来就是处理参数了,从区别可以知道,call
是可以有无数参数的,我们在第二步之前添加一个步骤。
先来看看真实 call
的测试。
1 | let value = 1; |
- 获取第二个参数开始的全部参数
1 | Function.prototype.myCall = function (context) { |
使用我们的 myCall
执行,发现结果是相同的。
最后版
在这里我们需要注意两个问题
- 传入的对象是
null
注意,我将外层的定义 let
改成了 var
,因为 var
设置了全局变量,如果设置 let
则会打印 undefined
,并没有在 window
对象中定义。
1 | var value = 1; |
执行代码,可以知道,传入 null
时,指向了我们的 window
对象。
- 对象可以有返回值
1 | var value = 1; |
- 最后代码全代码
首先处理传入 null 的时候,判断传入上下文对象是否存在,如果不存在,则设置为 window
。
处理第二个问题则是将方法执行结果进行保留,返回结果。
1 | Function.prototype.myCall = function (context) { |
测试一下我们自己写的 call
代码
1 | var value = 2; |
apply 实现
我们首先了解了三者的区别,我们发现 apply
与 call
的区别,就在于传入的参数,针对这一区别,我们开始实现 apply
,就只需要对 call
的代码进行部分修改即可。
- 判断调用对象是否为函数,即使我们是定义在函数的原型上的,但是可能出现使用
call
等方式调用的情况- 判断传入上下文对象是否存在,如果不存在,则设置为
window
- 将函数作为上下文对象的一个属性
- 判断参数值是否传入
- 使用上下文对象来调用这个方法,并保存返回结果
- 删除刚才新增的属性
- 返回结果
1 | Function.prototype.myApply = function (context, arr) { |
测试与 call
类似,只是修改了传入参数为数组, 打印结果相同。
1 | console.log(test.myApply(obj, ['hzq', 18])); |
文章参考
相关文章推荐
-------------------- 本文结束
感谢阅读 --------------------