手写 call、apply、bind
大约 2 分钟约 479 字
call()
、apply()
和 bind()
三者都可以改变 JavaScript 中的 this 指向,关于三者的区别在 this 指向中有详细记录。
这三个函数实际上是由 C++ 实现的,这里只考虑功能上的实现,不会考虑太多边界情况。
call()
call()
接受一个 this
指向,其后跟参数列表。
Function.prototype.myCall = function (thisArg, ...args) {
// 将 thisArg 转成对象类型(防止传入非对象类型)
// 传入 null 或 undefined 则为全局对象
thisArg = thisArg !== null && thisArg !== undefined ? Object(thisArg) : window
// foo.myCall();
// foo 是函数,本质上也是一个对象,即隐式绑定:对象(foo)调用函数(myCall)
// 那么函数 myCall 中的 this 指向 foo 对象(函数)
const fn = this // foo
// 需要将 fn 的指向改为 thisArg
// 那么可以继续利用 this 的隐式绑定规则
thisArg.fn = fn
const res = thisArg.fn(...args)
delete thisArg.fn
return res
}
apply()
apply()
与 call()
的区别在于传入的是参数数组,参照上面流程,可以很容易的写出:
Function.prototype.myApply = function (thisArg, args) {
// 处理 thisArg
thisArg = thisArg !== null && thisArg !== undefined ? Object(thisArg) : window
// 获取待执行函数
const fn = this
// 执行
thisArg.fn = fn
const res = thisArg.fn(...args)
delete thisArg.fn
return res
}
bind()
bind()
函数稍有不同,它返回一个改变了 this
的函数:
Function.prototype.myBind = function (thisArg, ...args) {
// 处理 thisArg
thisArg = thisArg !== null && thisArg !== undefined ? Object(thisArg) : window
// 获取待执行函数
const fn = this
return function _bind(...newArgs) {
// 收集参数
const finalArgs = [...args, ...newArgs]
// 执行
thisArg.fn = fn
const res = thisArg.fn(...finalArgs)
delete thisArg.fn
return res
}
}
总结
手写 call
、apply
、bind
的步骤大致相同,只是 bind
多了一个收集参数的过程:
- 处理 thisArg;
- 获取待执行函数;
- 执行函数(
bind()
还有一个收集参数的过程)。
无论是在获取函数还是执行函数(改变 this
)的过程中,都是利用了隐式绑定改变 this
指向的规则。