bind
bind 函数的实现,需要了解 this 的绑定。this 绑定有 4 种绑定规则: 默认绑定、 隐式绑定、 显示绑定、 new 绑定
Function.prototype.myBind = function (context) {
// 1. 保存原始函数
let that = this;
// 添加类型检查
if (typeof that !== "function") {
throw new TypeError("myBind must be called on a function");
}
// 2. 获取参数,从参数列表第一个开始获取,返回数组。获取除绑定的 this 之外的其他参数
let bindArgs = Array.prototype.slice.call(arguments, 1);
// 3. 返回一个新函数
function fBound() {
let args = Array.prototype.slice.call(arguments); // 获取调用时的参数
return that.apply(
this instanceof fBound ? this : context, // 如果是 new 调用,this 指向实例,否则指向 context
bindArgs.concat(args) // 合并预设参数和调用时参数
);
}
// 维护原型关系
if (this.prototype) {
// 此处获取原函数的原型,作为 fBound 的原型,维护原有的原型链
fBound.prototype = Object.create(this.prototype);
}
return fBound;
};
我们最开始要绑定的函数称为 fToBind, 返回值称为 fBound, 从代码可以看到, bind 执行的结果并没有返回 fTobind,真正的待执行函数被封装在 fBound 里面。当我们绑定完成之后执行 fBound 的时候,在 fBound 内部 fToBind 会调用 apply 进行绑定并执行。而此时,apply 的第一个参数已经是确定下来的了。接下来分为两种情况,当判断到当前函数是被当成构造函数执行时,此时忽略强行绑定,保留 new 操作;第二种情况是正常绑定,即获取 bind 函数传入的 this 进行绑定。
原因在于 bind 函数返回了一个新的函数 fBound,fBound 执行时,内部 fToBind 绑定所需要的 this 都已经确定下来了,当我们进行二次绑定时,操作的对象已经是 fBound 了,此时再进行 bind 操作时,我们只能改变 fBound 的 this,但函数不作为构造函数执行时 fBound 内部的绑定操作并不依赖于 fBound 的 this,而是依赖于第一次传入的 oThis。所以不管我们再怎么绑定,都不能再改变绑定的结果了。
简单来说,再次 bind 的时候,我们已经无法对最原始的待绑定函数进行操作了,我们操作的只是它的代理。
// bind() 方法会创建一个新函数。当这个新函数被调用时,bind() 的第一个参数将作为它运行时的 this,之后的一序列参数将会在传递的实参前传入作为它的参数。(来自于 MDN )
Function.prototype.bind = function (ctx, ...bindArgs) {
// 保存当前函数(this)
const fn = this;
if (typeof fn !== "function") {
throw new TypeError("myBind must be called on a function");
}
// 返回一个新函数
return function fBound(...newArgs) {
// 合并参数
return fn.apply(
this instanceof fBound ? this : ctx,
bindArgs.concat(newArgs)
);
};
};
最后更新于
这有帮助吗?