系列文章
说在前面的
call、apply 和 bind 是 js 中修改 this 指向的重要手段,在很多场景下都大有用处,其中 bind 和其余二者的现象区别是是否立即执行,而 call 和 apply 的现象区别是传参格式不同,其中的实现原理区别这里暂不做深入讨论。本文的核心是帮助大家从 0 到 1 实现自己的 call、apply 和 bind 函数,了解其实现原理。
实现
call
老规矩,我们先看看 call 是怎么样进行调用的:
可以看到,首先 call 是挂载在函数原型之上,让所有函数都可以通过 .call 的方式进行调用。然后 call 接收的第一个参数是一个 obj,即我们想改变的 this 指向对象。 那么这里的实现思路如下:
- 利用谁调用,this 就指向谁的机制,获取到调用 call 的函数的函数体
- 将要执行的函数体挂载到想要让 this 指向的结构体上
- 这样再次调用需要调用的函数方法,就已经调转了 this 指向
思路非常简单,就是通过调转函数挂载来调转 this 指向,不过这里还有一个问题,就是 call 是能接收参数的,这个问题很好解决,我们通过 ...args 接收参数,再通过 ...args 传给目标函数即可:
最后,我们知道,现在已经将函数挂载到结构体之上了,所以这里应该通过删除属性的方式,取消挂载,避免修改结构体:
值得一提的是,用 fn 命名,如果 obj 中也存在 fn,就会发生冲突,所以这里的 fn,更优解是使用 Symbol:
apply
apply 和 call 的实现几乎一模一样,除了 apply 接收参数的时候是用数组以外,那么这里基本把 call 的代码照抄过来就可以了:
bind
首先我们知道 bind 并不是立即执行的,所以可以确定,bind 内部一定是返回一个函数,所以外部才可以随时通过执行语句来执行:
这个返回的函数需要实现怎么样的功能呢?很明显,它只需要实现改变 this 指向,也就是改变 func 挂载即可:
然后,bind 能够接收参数,并且被 bind 修改过 this 指向的方法也能接收参数,所以这里关于参数需要考虑两个接收点:
这里选择的是利用 apply 来实现 bind,所以传参的时候结合成一个数组,如果选择用 call 来实现的话,直接将这个数组进行解构即可。