创见博客
手写实现bind/call/apply
七崽爱吃小饼干2026/01/22阅读 1专栏 JavaScript

手写实现 call、bind、apply 这三个函数的核心原理,和函数的隐式调用(也常说“上下文绑定”)密切相关,本质上就是通过改变函数执行时的 this 指向来实现的。

在看这篇文章之前可以先看上下文原理以及三种函数的介绍两篇文章

先理清核心逻辑

函数的 this 指向遵循一个基础规则:谁调用函数,函数的 this 就指向谁(隐式绑定)。 手写 call/apply/bind 的核心思路,就是利用这个规则——把要执行的函数临时挂载到目标对象上,让目标对象成为这个函数的调用者,从而让函数的 this 指向目标对象,执行完后再删除这个临时挂载的函数,避免污染目标对象。

分步骤拆解实现

1. 实现 call 函数

javascript
// 给 Function 原型扩展 myCall 方法
Function.prototype.myCall = function (context) {
  // 处理边界:如果 context 是 null/undefined,默认指向 window(浏览器环境)
  context = context || window;
  // 核心:把当前函数(this)挂载到 context 上(隐式调用的关键)
  // Symbol 保证属性名唯一,避免覆盖原有属性
  const fnKey = Symbol('tempFn');
  context[fnKey] = this;

  // 提取 call 的参数(第一个是 context,剩下的是函数参数)
  const args = [...arguments].slice(1);
  // 隐式调用:让 context 调用这个函数,this 自然指向 context
  const result = context[fnKey](...args);

  // 执行完删除临时属性,避免污染
  delete context[fnKey];
  // 返回函数执行结果(和原生 call 一致)
  return result;
};

2. 实现 apply 函数

apply 和 call 逻辑几乎一致,唯一区别是参数接收形式(数组/类数组):

javascript
Function.prototype.myApply = function (context) {
  context = context || window;
  const fnKey = Symbol('tempFn');
  context[fnKey] = this;

  // 核心区别:apply 第二个参数是数组,直接解构即可
  const args = arguments[1] || [];
  const result = context[fnKey](...args);

  delete context[fnKey];
  return result;
};

3. 实现 bind 函数

bind 稍特殊:它不立即执行函数,而是返回一个绑定了 this 的新函数,且支持柯里化传参:

javascript
Function.prototype.myBind = function (context) {
  const fn = this; // 保存原函数
  // 提取 bind 第一个参数后的参数(柯里化的第一部分)
  const bindArgs = [...arguments].slice(1);

  // 返回新函数(核心:调用新函数时,再用 apply 绑定 this)
  return function () {
    // 提取新函数调用时的参数(柯里化的第二部分)
    const callArgs = [...arguments];
    // 合并参数,用 apply 实现 this 绑定(还是依赖隐式调用)
    return fn.apply(context, bindArgs.concat(callArgs));
  };
};

验证

javascript
// 测试用例
const person = { name: '张三' };
function sayHi(age, gender) {
  console.log(`${this.name},${age}岁,${gender}`);
  return '执行完成';
}

// 测试 myCall
console.log(sayHi.myCall(person, 20, '男')); // 张三,20岁,男 → 执行完成

// 测试 myApply
console.log(sayHi.myApply(person, [20, '男'])); // 张三,20岁,男 → 执行完成

// 测试 myBind
const bindFn = sayHi.myBind(person, 20);
console.log(bindFn('男')); // 张三,20岁,男 → 执行完成

总结

  1. 手写 call/apply/bind 的核心:利用“函数的 this 指向调用者”(隐式绑定),把函数临时挂载到目标对象上,让目标对象调用该函数,从而改变 this 指向。
  2. call 和 apply 的区别:仅在于参数传递形式(call 是逐个传参,apply 是数组传参),核心逻辑完全一致。
  3. bind 的特殊点:不立即执行,返回一个绑定了 this 的新函数,且支持参数柯里化,底层依然依赖 apply/call 实现 this 绑定。

简单说,这三个方法的手写实现,本质就是“借隐式调用的规则,临时给目标对象加一个函数属性,调用后再删掉”。

评论
0/100