JS实现一个bind
第一层 - 绑定在原型上的方法
由于 function xxx 的原型链 指向的是 Function.prototype , 因此我们在调用 xxx.bind 的时候,调用的是 Function.prototype 上的方法。
Function.prototype._bind = function() {}

第二层 - 改变 this 的指向
Function.prototype._bind = function(thisObj) { const self = this; return function () { self.apply(thisObj); } }
测试
var obj = {a:1} function myname() {console.log(this.a)} myname._bind(obj)(); // 1
第三层 - 支持柯里化
通过获取当前外部函数的 arguments ,并且去除了绑定的对象,保存成变量 args,最后 return 的方法,再一次获取当前函数的 arguments, 最终用 finalArgs 进行了一次合并
Function.prototype._bind = function(thisObj) { const self = this; const args = [...arguments].slice(1) return function () { const finalArgs = [...args, ...arguments] self.apply(thisObj, finalArgs); } }
测试
var obj = { i: 1} function myFun(a, b, c) { console.log(this.i + a + b + c); } var myFun1 = myFun._bind(obj, 1, 2); myFun1(3); // 7
第四层 - 考虑 new 的调用
通过 bind 绑定之后,依然是可以通过 new 来进行实例化的, new 的优先级会高于 bind
// 原生 var obj = { i: 1} function myFun(a, b, c) { // 此处用new方法,this指向的是当前函数 myFun console.log(this.i + a + b + c); } var myFun1 = myFun.bind(obj, 1, 2); new myFun1(3); // NAN // 第四层的 bind var obj = { i: 1} function myFun(a, b, c) { console.log(this.i + a + b + c); } var myFun1 = myFun._bind(obj, 1, 2); new myFun1(3); // 7
注意,这里使用的是 bind方法
因此我们需要在 bind 内部,对 new 的进行处理。而 new.target 属性,正好是用来检测构造方法是否是通过 new 运算符来被调用的。
而根据 MDN,new 关键字会进行如下的操作:
- 创建一个空的简单JavaScript对象(即
{}); - 链接该对象(设置该对象的constructor)到另一个对象 ;
- 将步骤1新创建的对象作为
this的上下文 ; - 如果该函数没有返回对象,则返回
this。
Function.prototype._bind = function(thisObj) { const self = this; const args = [...arguments].slice(1); return function () { const finalArgs = [...args, ...arguments]; // new.target 用来检测是否是被 new 调用 if(new.target !== undefined) { // this 指向的为构造函数本身 var result = self.apply(this, finalArgs); // 判断改函数是否返回对象 if(result instanceof Object) { return reuslt; } // 没有返回对象就返回 this return this; } else { // 如果不是 new 就原来的逻辑 return self.apply(thisArg, finalArgs); } } }
第五层 - 保留函数原型
当我们的构造函数有 prototype 属性的时候,就出问题啦。因此我们需要给 prototype 补上,还有就是调用对象必须为函数
Function.prototype._bind = function (thisObj) { // 判断是否为函数调用 if (typeof target !== 'function' || Object.prototype.toString.call(target) !== '[object Function]') { throw new TypeError(this + ' must be a function'); } const self = this; const args = [...arguments].slice(1); var bound = function () { var finalArgs = [...args, ...arguments]; // new.target 用来检测是否是被 new 调用 if (new.target !== undefined) { // 说明是用new来调用的 var result = self.apply(this, finalArgs); if (result instanceof Object) { return result; } return this; } else { return self.apply(thisArg, finalArgs); } }; if (self.prototype) { // 为什么使用了 Object.create? 因为我们要防止,bound.prototype 的修改而导致self.prototype 被修改。不要写成 bound.prototype = self.prototype; 这样可能会导致原函数的原型被修改。 bound.prototype = Object.create(self.prototype); bound.prototype.constructor = self; } return bound; };
参考:https://github.com/Raynos/function-bind

浙公网安备 33010602011771号