关于手写 call,apply 与 bind,我有如下总结,因为我也是看完课程后的总结。所以学之前,先建议看看黑马新出的前端面试题中关于call,apply与bind,一起来看看吧~~~
call
call 是一个函数,可以改变函数的this 指向,先来看看 call 的基本使用吧!
1 2 3 4 5 6 7 8 9
| let obj = { name: "刘德华" };
function fn(num1, num2) { console.log(this); return num1 + num2; }
const res = fn.call(obj, 1, 2); console.log("两数之和为", res);
|
可以看出 call 函数改变了 fn 函数的 this 指向变为 obj,并且可以传递参数序列。
第一步(改变 this)
接下来我将定义一个函数 myCall 来实现 call 所具有的逻辑:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
|
Function.prototype.myCall = function (outObj, ...args) { outObj.f = this; outObj.f(); }; let obj = { name: "刘德华" }; function fn(num1, num2) { console.log(this); return num1 + num2; } const res = fn.myCall(obj, 1, 2); console.log("两数之和为", res);
|
详细讲解,看上方代码注释!!!上述代码的 f (中转键),有可能与传入对象重名,这样肯定是不严谨的,所以我使用 Symbol:
第二步(Symbol 优化)
symbol 是一种基本数据类型,每个从 Symbol() 返回的 symbol 值都是唯一的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| Function.prototype.myCall = function (outObj, ...args) { let key = Symbol("key"); outObj[key] = this; const res = outObj[key](...args); delete outObj[key]; return res; }; let obj = { name: "刘德华" }; function fn(num1, num2) { console.log(this); return num1 + num2; } const res = fn.myCall(obj, 1, 2); console.log("两数之和为", res);
|
上述代码使用delete outObj[key]
就是清除向对象里面追加的fn 函数。如下:

第三步(考虑其他情况)
根据 call 方法:当你要改变 this 的指向为 bull 或 undefind 时,fn 函数的 this 指向将会变为Window

所以我有了自己的想法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| Function.prototype.myCall = function (outObj, ...args) { let key = Symbol("key"); if (outObj === null || outObj === undefined) { window[key] = this; const res = window[key](...args); delete window[key]; return res; } else { outObj[key] = this; const res = outObj[key](...args); delete outObj[key]; return res; } }; let obj = { name: "刘德华" }; function fn(num1, num2) { console.log(this); return num1 + num2; } const res = fn.myCall(obj, 1, 2); console.log("两数之和为", res);
|
apply
apply 与 call 的区别就在于,传递的参数形式不同,掌握 call,对于 apply 也就差不多了。同样定义函数 myApply:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| Function.prototype.myApply = function (outObj, arr) { let key = Symbol("key"); if (outObj === null || outObj === undefined) { window[key] = this; const res = window[key](...arr); delete window[key]; return res; } else { outObj[key] = this; const res = outObj[key](...arr); delete outObj[key]; return res; } }; let obj = { name: "xjy" }; function fn(num1, num2) { console.log(this); return num1 + num2; } const res = fn.myApply(undefined, [1, 2]); console.log("两数之和为", res);
|
同样也考虑了改变 this 的指向为 bull 或 undefind 时的情况
bind
bind 可能有两种方式:
第一种
第一种就是上述 call 与 apply 的实现方式一样
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| Function.prototype.myBind = function (outObj, ...a) { let key = Symbol("key"); if (outObj === null || outObj === undefined) { window[key] = this; return (...b) => { const res = window[key](...a, ...b); delete window[key]; return res; }; } else { outObj[key] = this; return (...b) => { const res = outObj[key](...a, ...b); delete outObj[key]; return res; }; } }; let obj = { name: "xjy" }; function fn(num1, num2, num3) { console.log(this); return num1 + num2 + num3; } const res = fn.myBind(null, 1, 2, 10); console.log("两数之和为", res(3));
|
上述代码之所以 return 一个回调函数,是因为 bind 调用不是立即执行,然后后面传递的参数是优先于const res = fn.myBind(null, 1, 2, 10)
后再console.log('两数之和为', res(3));
所以有一定的顺序传递。
第二种
这种方式比较简便,在使用了一次 call 改变 this 指向。
1 2 3 4 5 6 7 8 9 10 11 12 13
| Function.prototype.myBind = function (outObj, ...a) { return (...b) => { return this.call(outObj, ...a, ...b); }; }; let obj = { name: "xjy" }; function fn(num1, num2, num3) { console.log(this); return num1 + num2 + num3; } const res = fn.myBind(obj, 1, 2, 10); console.log("两数之和为", res(3));
|
这里不需要考虑 null 与 undefind 的原因是,使用了 call 方法,上述已经讲过 call 本生改变 this 指向为 null 与 undefind 时,原函数的 this 就会指向 window。
最后
上述就是手写 call,apply 与 bind 的全部内容,本人也是观看视频写下的总结!,上述内容可能还有缺陷,欢迎指出。