前端面试宝典

JS 中 bind、call、apply 超清晰讲解(新手一看就懂)

你想彻底搞懂 JS 里 bind、call、apply 这三个高频面试点+开发常用方法,我用最通俗的语言 + 核心原理 + 极简示例 + 区别总结,一次性讲透,不绕弯子。

一、先搞懂:它们到底是干嘛的?

一句话核心:
这三个方法,都是用来强行改变函数中 this 的指向!

JS 里 this 指向很灵活,谁调用函数,this 就指向谁。
但有时候我们想手动指定 this 是谁,就用 call / apply / bind

它们都是 Function 原型上的方法,所有函数都能直接用。


二、先看最简单的:call(立即执行)

语法

函数.call(新的this指向, 参数1, 参数2, 参数3...)

特点

  1. 立即调用函数
  2. 第一个参数是 this 要指向的对象
  3. 后面的参数一个一个传

示例

const person = { name: "小明" };

function sayHi(age, city) {
  console.log(`我是${this.name},今年${age}岁,住在${city}`);
}

// 强行让 sayHi 里的 this 变成 person
sayHi.call(person, 18, "北京");
// 输出:我是小明,今年18岁,住在北京

三、apply(和 call 几乎一样,只是传参不同)

语法

函数.apply(新的this指向, [参数1, 参数2, 参数3...])

特点

  1. 立即调用函数
  2. 第一个参数 = this 指向
  3. 第二个参数必须是数组/类数组

示例

const person = { name: "小红" };

function sayHi(age, city) {
  console.log(`我是${this.name},今年${age}岁,住在${city}`);
}

// apply 必须把参数包在数组里
sayHi.apply(person, [20, "上海"]);
// 输出:我是小红,今年20岁,住在上海

四、bind(不立即执行,返回一个新函数)

语法

const newFunc = 函数.bind(新的this指向, 参数1, 参数2...)

特点

  1. 不会立即执行
  2. 返回一个永久绑定 this 的新函数
  3. 参数和 call 一样,一个一个传

示例

const person = { name: "小刚" };

function sayHi(age, city) {
  console.log(`我是${this.name},今年${age}岁,住在${city}`);
}

// 生成一个新函数,this 永远指向 person
const boundFunc = sayHi.bind(person, 22);

// 想什么时候执行,就什么时候执行
boundFunc("深圳");
// 输出:我是小刚,今年22岁,住在深圳

五、超级清晰对比表(面试必背)

方法是否立即执行参数传递方式返回值
call✅ 立即执行单个参数依次传递函数原返回值
apply✅ 立即执行所有参数放在一个数组函数原返回值
bind❌ 不执行单个参数依次传递绑定 this 后的新函数

记忆口诀

  • call 逗号传
  • apply 数组传
  • bind 不执行,返回新函数

六、最常用的实战场景(必看)

1. 利用 apply 求数组最大值/最小值

const arr = [3, 1, 88, 22];
const max = Math.max.apply(Math, arr); // 88
const min = Math.min.apply(Math, arr); // 1

2. 数组伪数组转真数组

const divs = document.querySelectorAll('div');
const arr = Array.prototype.slice.call(divs);

3. 事件/定时器里修正 this

const obj = {
  name: "测试",
  fn() {
    // 定时器里 this 会变,用 bind 固定
    setTimeout(function() {
      console.log(this.name);
    }.bind(this), 1000);
  }
};

七、最简单的总结(背会就够用)

  1. call、apply、bind 都能改 this
  2. call / apply 马上运行bind 返回函数稍后运行
  3. call 逐个传参apply 数组传参
  4. bind 常用于事件、定时器固定 this

总结

  • call:立即执行,参数逗号分隔
  • apply:立即执行,参数放数组
  • bind:不执行,返回绑定 this 的新函数
  • 核心作用:强行改变函数内部 this 的指向

面试题1

var myObj1 = {
    name: '小王',
    myAge: this.age,
    sayName: function(add, front) {
        console.log(this.name + '今年' + this.age + '在' + add + '干' + front);
    }
}

var heros = {
    name: '小张',
    age: '20'
}

myObj1.sayName.call(heros, '上海', '前端'); // 小张今年20在上海干前端
myObj1.sayName.apply(heros, ['上海', '前端']); // 小张今年20在上海干前端
myObj1.sayName.bind(heros, ['上海', '前端'])(); // 小张今年20在上海,前端干undefined
myObj1.sayName.bind(heros, '上海', '前端')(); // 小张今年20在上海干前端
myObj1.sayName.bind(heros)('上海', '前端'); // 小张今年20在上海干前端

面试题2

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>apply_call_bind</title>
  </head>
  <body>
    <script>
      // 4. apply、call、bind来调用
      function t() {
        console.log(this.x);
      }

      t.bind({ x: 6 })();

      // 手写bind函数
      Function.prototype.myBind = function (context, ...args) {
        const fn = this;
        return function (...innerArgs) {
          return fn.apply(context, [...args, ...innerArgs]);
        };
      };
      t.myBind({ x: 6 })();

      // 手写apply函数
      // 给所有函数添加myApply方法,用于改变函数执行时的this指向
		Function.prototype.myApply = function (context, args) {
		  // 处理上下文:若context为null/undefined,非严格模式下默认指向全局对象window
		  context = context || window;
		  // 将当前调用myApply的函数(this)挂载到context的临时属性fn上,使调用时this指向context
		  context.fn = this;
		  // 执行挂载在context上的函数,用扩展运算符展开args参数(若args不存在则用空数组),并接收返回结果
		  const result = context.fn(...(args || []));
		  // 执行后删除临时添加的fn属性,避免污染上下文对象
		  delete context.fn;
		  // 返回函数执行的结果
		  return result;
		};
      t.myApply({ x: 6 });

      // 手写call函数
      Function.prototype.myCall = function (context, ...args) {
        context = context || window;
        context.fn = this;
        const result = context.fn(...args);
        delete context.fn;
        return result;
      };
    </script>
  </body>
</html>