出处:掘金

原作者:金泽宸


  • call:立即执行 + 传参列表
  • apply:立即执行 + 传参数组
  • bind:返回一个新函数,不立即执行;绑定 this 和前几个参数

为什么要手写这几个函数?

  • 它们是 JavaScript 中 Function 原型链上非常核心的函数
  • 实际开发中经常用于改变函数的执行上下文(this
  • 面试高频必考,考你理解 this、本质执行流程、原型继承

核心思路

fn.call(obj, arg1, arg2)
 
// 等价于:
obj.fn = fn
obj.fn(arg1, arg2)
delete obj.fn

手写 call

Function.prototype.myCall = function (context, ...args) {
  // 1. 处理 context 的边界情况(null/undefined/原始值)
  if (context === null || context === undefined) {
      context = globalThis; // 非严格模式下,默认指向全局对象
  } else if (typeof context !== 'object') {
      context = new Object(context); // 原始值转换为包装对象(如数字 1 → Number 对象)
  }
 
  // 2. 用 Symbol 创建唯一属性,避免覆盖 context 原有属性
  const uniqueThis = Symbol('unique');
  context[uniqueThis] = this; // this 是当前函数
 
  // 3. 调用函数,此时函数内部的 this 指向 context
  const result = context[uniqueThis](...args);
 
  // 4. 删除临时属性,避免污染 context
  delete context[uniqueThis];
 
  // 5. 返回函数执行结果
  return result;
};

手写 apply

Function.prototype.myApply = function (context, args) {
  // 1. 处理 context 的边界情况(null/undefined/原始值)
  if (context === null || context === undefined) {
      context = globalThis; // 非严格模式下,默认指向全局对象
  } else if (typeof context !== 'object') {
      context = new Object(context); // 原始值转换为包装对象(如数字 1 → Number 对象)
  }
  
  // 2. 用 Symbol 创建唯一属性,避免覆盖 context 原有属性
  const uniqueThis = Symbol('unique');
  context[uniqueThis] = this; // this 是当前函数
  
  // 3. 调用函数,此时函数内部的 this 指向 context
  const result = Array.isArray(args)
    ? context[uniqueThis](...args)
    : context[uniqueThis]();
  
  // 4. 删除临时属性,避免污染 context
  delete context[uniqueThis];
  
  // 5. 返回函数执行结果
  return result;
};

手写 bind

基础版

Function.prototype.myBind = function (context, ...outArgs) {
  // 1. 判断 context 类型
  if (context === undefined || context === null) {
    context = globalThis;
  } else {
    context = Object(context);
  }
 
  // 2.唯一属性保存
  const uniquePrototype = Symbol("unique");
  context[uniquePrototype] = this;
 
  // 3.返回调用的函数
  return function (...innerArgs) {
    return context[uniquePrototype](...outArgs, ...innerArgs);
  };
};

缺陷:

  • 没删除 Symbol("unique"),不能删除,删除后返回的函数无法重复执行
  • 不支持构造函数绑定:与原生的 bind 不同,这个自定义的实现不支持通过 new 调用绑定函数以创建对象。如果尝试使用 new 来实例化绑定函数,会导致错误或意外的行为
  • 不支持原型继承: 当使用原生的 bind 函数绑定函数时,绑定函数和原函数之间共享原型。然而,这个自定义的实现并没有处理这种情况,导致原型继承相关的特性无法正确传递
  • 不支持 length 属性: 在 JavaScript 中,函数对象具有一个 length 属性,它返回函数定义的参数数量。使用原生的 bind 函数绑定后的函数会保留正确的 length 属性,但这个自定义实现并没有处理 length 属性,它会返回原始函数的参数数量,而不是绑定函数的参数数量
  • 不支持取消绑定: 原生的 bind 函数返回的绑定函数是可以取消绑定的(通过使用 Function.prototype.unbind 或 ES6 的 Function.prototype.@@unbound),但是这个自定义的实现并没有提供取消绑定的方法

进阶版

Function.prototype.myBind = function (context, ...outArgs) {
  const self = this;
 
  function boundFunction(...innerArgs) {
    // 如果是用 new 调用
    if (this instanceof boundFunction) {
      return new self(...outArgs, ...innerArgs);
    }
    return self.apply(context, [...args, ...restArgs]);
  }
 
  // 原型继承
  boundFunction.prototype = Object.create(this.prototype);
 
  // 添加 unbind 方法,返回原始函数
  boundFunction.unbind = () => self;
 
  return boundFunction;
};

常见易错点

错误理解正确做法
bind 会立即执行❌,它返回一个函数,不会立即执行
没有处理 new 场景✅ 用 this instanceof 区分
覆盖对象属性名✅ 用 Symbol 解决