手动实现call、apply、bind
先写一个骨架
1 Object.assign(Function.prototype, { 2 myCall, 3 myApply, 4 myBind, 5 }); 6 7 function myCall(_this, ...arg) {} 8 9 function myApply(_this, arg) {} 10 11 function myBind(_this, ...arg1) {}
myCall
原理:改变调用者
1 function myCall(_this, ...arg) { 2 // 注意: 3 // 1. 使用 Symbol 作为 key,避免造成意外覆盖 4 // 2. 要有 return,否则原始函数的执行结果无法暴露 5 6 const key = Symbol(); 7 _this[key] = this; 8 const res = _this[key](...arg); 9 delete _this[key]; 10 11 return res; 12 }
完善:允许第一个参数是任意值
1 function myCall(_this, ...arg) { 2 // _this 允许传任意值 3 4 if (_this === null || _this === undefined) { 5 return this(...arg); 6 } 7 8 if (!(typeof _this == "object")) { 9 _this = Object(_this); 10 } 11 12 // 注意: 13 // 1. 使用 Symbol 作为 key,避免造成意外覆盖 14 // 2. 要有 return,否则原始函数的执行结果无法暴露 15 16 const key = Symbol(); 17 _this[key] = this; 18 const res = _this[key](...arg); 19 delete _this[key]; 20 21 return res; 22 }
myApply
1 function myApply(_this, arg) { 2 // apply 的第二个参数限定为数组;其它具备 iterator 的数据结构也行 3 if (!(arg instanceof Array)) { 4 throw new Error("Please enter an array"); 5 } 6 7 return this.myCall(_this, ...arg); 8 }
myBind
分析:原生 bind 返回一个绑定了 _this 和 部分参数的新函数
1 function myBind(_this, ...arg1) { 2 const target = this; 3 4 function res(...arg2) { 5 return target.myApply(_this, [...arg1, ...arg2]); 6 } 7 8 return res; 9 }
测试
设计一个比较好的测试,用更少的代码,测试更多的功能
1 function foo(a, b, c) { 2 return [this === obj, a, b, c]; 3 } 4 5 const obj = {}; 6 7 console.log(foo.myCall(obj, "myCall", 0, 1)); 8 console.log(foo.myApply(obj, ["myApply", 0, 1])); 9 console.log(foo.myBind(obj, "myBind").myBind(null, 0)(1));
结尾
需要bind的人多了,也就有了bind