javascript bind 踩坑与原理

395 阅读1分钟

前置知识:原型链、new 的原理、callapply 的使用

简单版:

// 1. 不要覆盖原有的 bind 方法,否则会造成意想不到的问题
Function.prototype.myBind = function (obj, ...rest) {
    if (typeof this !== 'function') {
        throw new Error('请使用一个函数调用 bind')
    }
    const selfFn = this
    // 2. 如此简单的处理会使得 返回函数 作为 构造函数 时失效
    return function (...args) {
        selfFn.apply(obj, [...rest, ...args])
    }
}
  1. 不要试图修改不属于自己的对象的某一个属性,不要覆盖任何不属于自己创造的属性

    node 环境中出现的问题,如果使用 bind,而不是 myBind,我们在函数实现内部添加一个 console.log,就会导致爆栈,我猜测 console.log 的内部实现调用了 bind 方法,导致 console.log 的内部也会进行打印,并且上下文调用栈被不停的塞入上下文(该问题出现在 node 环境下,版本为:14.20.0;谷歌浏览器不会出现这个问题)。

  2. 当 bind 好的返回函数作为构造函数时,原始函数的原型链必须链接到新创建的对象,而不是原 bind 的目标,符合 new 的要求(new 绑定 this 的优先级高于硬绑定)

完整版:

Function.prototype.myBind = function (obj, ...rest) {
  if (typeof this !== "function") {
    throw new Error("请使用一个函数调用 bind");
  }
  const self = this
  function fnBound (...args) {
    // 此 this !== self,而是 fnBound 作为构造函数新创建的对象
    const _this = this instanceof self ? this : obj
    self.apply(_this, [...rest, ...args])
  }
  // 关于此处,我会补充另一种官方推荐的写法
  fnBound.prototype.__proto__ = self.prototype
  return fnBound
}

todo 补充概念:

  • new 的原理
  • bind 作为构造函数时与原型链的纠葛

以上两个问题会更新的。