JavaScript bind 的原理
什么是 bind 方法?
Function.prototype.bind 允许给一个函数显示的绑定上下文,执行时,执行时绑定的上下文
bind 的实现原理
js
Function.prototype.myBind = function(context, ...args) {
// 原始函数
const fn = this
// 处理上下文,null 或者 undefined 的时候,设置为全局对象
context = context || globalThis
function bindFunction(...innerArgs) {
// 通过 new 调用, this 应该指向新建的对象
// 拓展一点:函数的使用方式:./js-function-invoke.md
// instanceof 的原理: ./js-instanceof.md
// 再说透一点,如果是通过 new 来调用方法, 那么 new 出来的对象的 this 指向一定是 bindFunction 的实例,因为构造函数相同
if (this instanceof bindFunction) {
return new fn(...args, ...innerArgs)
}
// 否则,使用 context 作为 this 调用原始函数
return fn.apply(context, [...args, ...innerArgs])
// 也可以这么写
// return fn.apply(this instanceof bindFunction ? this : context, [...args, ...innerArgs])
}
// 复制原型
bindFunction.prototype = Object.create(fn.prototype)
// 以下是贴近原生的写法,把一些属性替换掉
// 复制函数属性 name length
Object.defineProperty(bindFunction, "name", {
value: `bind ${fn.name}`,
configurable: true
})
// 调整length 属性,要减去预绑定的参数的数量
Object.defineProperty(bindFunction, 'length', {
value: Math.max(0, fn.length - args.length)
})
return bindFunction
}使用示例
js
const obj = { name: 'Alice' }
function greet(msg) {
console.log(`${msg}, ${this.name}`)
}
const bindGreet = greet.myBind(obj, 'Hello')
bindGreet() // Hello, Alice
function Person(name, age) {
this.name = name
this.age = age
this.say = function() {
console.log(`I am ${this.name}, ${this.age} years old`)
}
}
const BindPeron = Person.myBind(null, 'Bob')
const person = new BindPeron(25)
console.log(person.age) // 25
console.log(person.say()) // I am Bob, 25 years old总结
bind实际上是通过闭包保存上下文和参数,并根据调用方式动态绑定this- 要重点关注处理
new的调用场景(通过instanceof检测) 和原型链的正确性(Object.create复制原型链)。通过这写处理,确保bind在普通调用时和构造函数调用时均能正常工作