这是我参与2022首次更文挑战的第4天,活动详情查看:2022首次更文挑战
第一层 - 绑定在原型上的方法
得益于 JS 原型链的特性。由于 function xxx 的原型链 指向的是 Function.prototype , 因此我们在调用 xxx.bind 的时候,调用的是 Function.prototype 上的方法。
Function.prototype._bind = function() {}
第二层 - 改变 this 的指向
bind 最核心的特性 - 改变 this 的指向+返回一个函数。
可以通过已知的 apply 和 call 来实现,这里我们就暂且使用 apply 来进行模拟。首先通过 self 来保存当前 this,也就是传入的函数。因为this 具有 隐式绑定的规则(
Function.prototype._bind = function(thisObj) {
const self = this;
return function () {
self.apply(thisObj);
}
}
第三层 - 支持柯里化
Function.prototype._bind = function(thisObj) {
const self = this;
const args = [...arguments].slice(1)
return function () {
const finalArgs = [...args, ...arguments]
self.apply(thisObj, finalArgs);
}
}
第四层 - 考虑 new 的调用
通过 bind 绑定之后,依然是可以通过 new 来进行实例化的, new 的优先级会高于 bind
因此需要自己实现一个 new ,
new关键字会进行如下的操作: 1.创建一个空的简单JavaScript对象(即{}); 2.链接该对象(设置该对象的constructor)到另一个对象 ; 3.将步骤1新创建的对象作为this的上下文 ; 4.如果该函数没有返回对象,则返回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 补上 - 调用对象必须为函数。
Function.prototype._bind = function (thisObj) {
// 判断是否为函数调用
if (typeof target !== 'function' || Object.prototype.toString.call(target) !== '[object Function]') {
throw newTypeError(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 instanceofObject) {
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;
};