手动实现 [ 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
-
原理:普通函数的 this 指向调用者,改变调用者,即改变 this 指向
-
功能实现
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 _this = global; 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
-
原理:复用 myCall
-
实现
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));
六、结尾
高阶层的 api 并非一步到位,它们的巧妙构思也是从低阶层的 api,一步步演化而来