Skip to content

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 在普通调用时和构造函数调用时均能正常工作