JavaScript 手写实现 call / apply / bind

17 阅读1分钟

不同环境的全局对象:

Node.js:

  • global === globalThis ✓
  • 没有 window

浏览器:

  • window === globalThis ✓
  • 没有 global

Web Worker:

  • self === globalThis ✓
  • 没有 window 和 global

总结: globalThis 在所有环境中都指向该环境的全局对象

// 手写实现call-apply-bind函数

// 将 myCall 添加到 Function.prototype 上
Function.prototype.myCall = function (context, ...args) {
  if (typeof this !== 'function') {
    throw new Error('myCall must be called on a function')
  }

  // 如果 context 为 null 或 undefined,则指向全局对象
  context = context || globalThis
  // context = context || (typeof window !== 'undefined' ? window : global)

  // 使用 Symbol 避免属性名冲突
  const fn = Symbol('fn')
  context[fn] = this
  const result = context[fn](...args)
  delete context[fn]
  return result
}

// 将 myApply 添加到 Function.prototype 上
Function.prototype.myApply = function (context, args) {
  if (typeof this !== 'function') {
    throw new Error('myApply must be called on a function')
  }

  context = context || globalThis

  const fn = Symbol('fn')
  context[fn] = this
  const result = context[fn](...args)
  delete context[fn]
  return result
}

// 将 myBind 添加到 Function.prototype 上
Function.prototype.myBind = function (context, ...args) {
  if (typeof this !== 'function') {
    throw new Error('myBind must be called on a function')
  }

  const self = this
  return function (...innerArgs) {
    return self.myApply(context, [...args, ...innerArgs])
    // return self.myCall(context, ...args, ...innerArgs)

    /**
     * context = context || globalThis
     * const fn = Symbol('fn')
     * context[fn] = self
     * const result = context[fn](...args, ...innerArgs)
     * delete context[fn]
     * return result
     */
  }
}

// 使用myCall
function test(a, b) {
  console.log(this.value, a, b)
}

const obj = { value: 1 }
test.myCall(obj, 2, 3)
test.myApply(obj, [2, 3])
const bound = test.myBind(obj)
bound(2, 3)