大漠的博客

可远程工作的全栈工程师 | 支持北美/欧洲时区

call、apply和bind

在 JavaScript 中,callapplybind 是函数对象的三个方法,它们都用于显式地设置函数调用时的 this(即改变函数内部的上下文)。虽然功能相似,但它们在调用方式和参数传递上有所不同。

1.call

语法:

func.call(thisArg, arg1, arg2, ...)

特点:

  • 立即执行函数。
  • 参数以逗号分隔的形式传入。

手写示例:

Function.prototype.Call = function(context,...args){
  // 1. 类型检查
  if (typeof this !== 'function') {
    throw new TypeError(this + ' is not a function');
  }
  // 2. 处理 null/undefined
  if (context == null) {
    context = globalThis;
  } else {
    // 3. 将原始值包装为对象
    context = Object(context);
  }
  // 4.创建一个symbol,目的是避免键名fn和context上的键名重复
  const fn = Symbol()
  // 5.把函数(this)暂时存放到context上,这是的this是调用call的函数
  context[fn] = this
  // 6.执行函数
  const result = context[fn](...args)
  // 7.删除掉临时存放的函数
  delete context[fn]
  return result
}

2.apply

语法:

func.apply(thisArg, [argsArray])

特点:

  • 立即执行函数。
  • 第二个参数是一个数组(或类数组),用于传递参数。

手写示例:

Function.prototype.Applay = function(context,args){
  // 1. 类型检查
  if (typeof this !== 'function') {
    throw new TypeError(this + ' is not a function');
  }
  // 2. 处理 null/undefined
  if (context == null) {
    context = globalThis;
  } else {
    // 3. 将原始值包装为对象
    context = Object(context);
  }
  // 4.创建一个symbol,目的是避免键名fn和context上的键名重复
  const fn = Symbol()
  // 5.把函数(this)暂时存放到context上,这是的this是调用call的函数
  context[fn] = this
  // 6.执行函数
  const result = context[fn](...args)
  // 7.删除掉fn
  delete context[fn]
  return result
}

💡 小技巧:当你有一组参数存在数组中,又想传给函数,用 apply 很方便。不过 ES6 后也可以用扩展运算符配合 callgreet.call(person, ...['Hi', '.'])

3. bind

语法:

const newFunc = func.bind(thisArg, arg1, arg2, ...);

特点:

  • 不立即执行,而是返回一个新函数,该函数的 this 被永久绑定到指定对象。
  • 可以预设部分参数(柯里化/偏函数应用)。

手写示例:

Function.prototype.Bind = function(context,...contextArgs){
  // 1. 类型检查
  if (typeof this !== 'function') {
    throw new TypeError(this + ' is not a function');
  }
  // 2.存放调用Bind方法的函数
  const fn = this; 
  // 3. 处理 null/undefined
  if (context == null) {
    context = globalThis;
  } else {
    // 4. 将原始值包装为对象
    context = Object(context);
  }

  const boundFunction = function(...args){
    // 5.判断Bind后的函数
    // 如果new了,this就是它自己,如果是普通函数调用就是context
    const thisArg = this instanceof fn ? this : context;
    return fn.apply(thisArg, contextArgs.concat(args));
  }
  // 6.判断fn上有原型对象,那么Bind后的函数,也应该继承它
  if(fn.prototype){
    boundFunction.prototype = Object.create(fn.prototype)
  }
  return boundFunction;
}

⚠️ 注意:bind 返回的新函数,即使再用 callapply 改变 this,也不会生效(因为 this 已被硬绑定)。

对比总结

方法是否立即执行参数形式返回值主要用途
call✅ 是逐个参数函数执行结果临时改变 this 并调用
apply✅ 是参数数组函数执行结果参数为数组时改变 this 调用
bind❌ 否逐个参数(可预设)新函数创建 this 固定的新函数

实际应用场景

  • 借用方法:比如用 Array.prototype.slice.call(arguments) 把类数组转为真数组。
  • 事件处理:在回调中保持正确的 this(如 btn.addEventListener('click', handler.bind(this)))。
  • 函数柯里化:通过 bind 预设部分参数,生成更具体的函数。

思考部分(ps:没事找事部分):

问题1:bind方法实现要考虑继承原型链,为什么call和apply不用考虑

问题2:为什么使用bind方法后的函数,再使用call和apply,他的this不会再改变

Tags:

发表回复

Your email address will not be published. Required fields are marked *.

*
*

技术栈

Nestjs,PHP,Angular,React,Vue

联系我

地址
123 Main Street
New York, NY 10001

营业时间
星期一—五:9:00–17:00
星期六—日:11:00–15:00