持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第 5 天,点击查看活动详情
在上一篇文章深入理解 this 指向中提到了 call()、apply() 和 bind() 都能改变 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.bind = 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 指向的规则。