外观
怎么手写 bind?
bind 返回一个新函数,把 this 和一部分参数固定下来。新函数被 new 调用时,this 改指向实例,预先传入的参数仍然有效。
原理
js
Function.prototype.myBind = function (context, ...preset) {
if (typeof this !== "function") {
throw new TypeError("myBind must be called on a function")
}
const fn = this
function bound(...args) {
return fn.apply(new.target ? this : context, preset.concat(args))
}
if (fn.prototype) {
bound.prototype = Object.create(fn.prototype)
}
return bound
}普通调用走 fn.apply(context, ...)。new bound() 时 new.target 有值,此时用构造出来的 this,忽略绑定时的 context。
bound.prototype 接到原函数的原型上,new bound() 的实例才能通过 instanceof 原构造函数。这里用 Object.create,避免 bound.prototype = fn.prototype 让两边共享同一个对象、后来的修改互相影响。
箭头函数没有自己的 this,也没有 prototype。对它调用 bind 可以固定参数,但不能给它装上一个调用时的 this。
追问
和 call / apply 的差别?
call 和 apply 立刻调用。bind 返回函数,适合交给事件或定时器稍后再调。call 逐个收参数,apply 收数组。
绑定之后再绑定?
原生 bind 的 this 只认第一次。上面的实现如果对 bound 再 myBind,外层是新的函数,行为要单独说明。面试里先把一次绑定和 new 讲对。
易错点
- 忘记处理
new,会导致new bound()的实例属性写到了当初绑定的那个对象上。 fn.prototype可能不存在(箭头函数)。直接Object.create(fn.prototype)会抛错。- 严格模式下独立调用时
this可能是undefined。判断是不是new,用new.target,不要写this instanceof bound。