← 返回文章列表
JavaScript·2571 次阅读

手写 call、apply 与 bind

手写 call、apply 与 bind

理解 this 之后,手写这三个方法是经典进阶题。核心思路一句话:把函数挂到目标对象上,作为对象的方法调用,this 自然就是它。

手写 call

Function.prototype.myCall = function (ctx, ...args) {
  ctx = ctx == null ? globalThis : Object(ctx) // null/undefined 指向全局;原始值包装
  const key = Symbol() // 唯一键,避免覆盖原有属性
  ctx[key] = this // this 就是调用 myCall 的那个函数
  const result = ctx[key](...args) // 隐式绑定,函数内 this = ctx
  delete ctx[key] // 用完清理
  return result
}

greet.myCall({ name: 'Dong' }, 1, 2)

手写 apply

与 call 完全一样,只是参数以数组形式接收:

Function.prototype.myApply = function (ctx, args = []) {
  ctx = ctx == null ? globalThis : Object(ctx)
  const key = Symbol()
  ctx[key] = this
  const result = ctx[key](...args)
  delete ctx[key]
  return result
}

手写 bind

bind 要复杂些:返回新函数,且参数可以分两次传入(柯里化):

Function.prototype.myBind = function (ctx, ...prevArgs) {
  const fn = this
  return function (...nextArgs) {
    return fn.myApply(ctx, [...prevArgs, ...nextArgs])
  }
}

处理 new 的边界

原生 bind 有个细节:返回的函数如果被 new 调用,this 应指向新实例而非绑定值。判断依据是 new.target(或 this instanceof fn):

function bound(...nextArgs) {
  const isNew = typeof new.target !== 'undefined'
  return fn.myApply(isNew ? this : ctx, [...prevArgs, ...nextArgs])
}
// 还要让实例能继承原函数原型上的方法:
bound.prototype = Object.create(fn.prototype)

这三个手写题考的本质都是同一件事:this 由调用方式决定,而"对象.方法()"是制造 this 最趁手的工具。

看到这里了?如果这篇文章帮到过你

请我喝瓶可乐就好 🥤

收款码

评论0 条

还没有评论,来抢沙发~

发表评论

评论需审核后显示

© 2026 许冬博客 · 专注 JS 技术分享

·RSS 订阅·Powered by Nuxt 4