标准答案

  1. call 接收 thisArg 和参数列表,apply 接收 thisArg 和参数数组。
  2. 实现 call/apply 可以临时把函数挂到 thisArg 上调用,再删除临时属性。
  3. bind 不会立即执行,而是返回一个新函数,调用时合并预置参数和本次参数。
  4. bind 返回的函数如果被 new 调用,this 应该指向新实例,而不是绑定时传入的对象。
  5. 实现时要处理 null、undefined thisArg,以及避免临时属性名冲突。

题目解析

这类手写题本质是在考函数调用时 this 怎么确定。call/apply 是把一次调用的接收者改掉,bind 则是提前做一层包装,把未来调用的 this 和部分参数固定下来。

call/apply 的简化实现可以借助“对象方法调用时 this 指向对象”这个规则。把函数临时放到目标对象上执行,就能模拟显式绑定。

bind 的 new 场景是常见加分点。绑定函数作为构造函数使用时,实例创建规则优先,不能再把 this 强行指向原来绑定的对象。

代码示例

下面是简化版 call 和 bind,重点展示 this 绑定思路。

JavaScript
Function.prototype.myCall = function myCall(thisArg, ...args) {
  const context = thisArg == null ? globalThis : Object(thisArg)
  const key = Symbol('fn')

  context[key] = this
  const result = context[key](...args)
  delete context[key]

  return result
}

Function.prototype.myBind = function myBind(thisArg, ...presetArgs) {
  const fn = this

  function bound(...args) {
    const isNewCall = this instanceof bound
    return fn.apply(isNewCall ? this : thisArg, [...presetArgs, ...args])
  }

  bound.prototype = Object.create(fn.prototype)
  return bound
}

常见误区

  • bind 实现成直接执行函数,而不是返回新函数。
  • 没有处理 bind 后再 new 的情况。构造调用时 this 应该是新实例。
  • 用普通字符串做临时属性名,可能覆盖目标对象已有属性。

作者信息