javascript技巧

关注公众号 jb51net

关闭
首页 > 网络编程 > JavaScript > javascript技巧 > JavaScript中call、apply、bind详解

一文详解JavaScript中 call、apply、bind

作者:yqcoder

在 JavaScript 开发中,this 的指向问题一直是初学者的痛点,而 call、apply 和 bind 则是解决这一问题的三把钥匙,虽然目的相同,但它们的用法和返回结果各有不同,本文将带你从原理到实战,一次性掌握它们,需要的朋友可以参考下

引言

在 JavaScript 开发中,this 的指向问题一直是初学者的痛点,而 callapplybind 则是解决这一问题的三把“钥匙”。它们都属于 Function.prototype 上的方法,核心作用都是改变函数执行时的上下文(即 this 指向)

虽然目的相同,但它们的用法和返回结果各有不同。本文将带你从原理到实战,一次性掌握它们。

1. 核心概念:什么是“借用”?

想象一下,你有一个工具箱(对象 A),里面有一把锤子(方法)。现在你想用这把锤子去敲钉子,但你不想把整个工具箱搬过来,或者你当前所在的场景(对象 B)没有锤子。

2. 语法与区别速览

特性callapplybind
第一个参数thisArg (新的 this 指向)thisArg (新的 this 指向)thisArg (新的 this 指向)
后续参数逐个传递 (arg1, arg2, ...)数组形式传递 ([arg1, arg2, ...])逐个传递 (arg1, arg2, ...)
返回值立即执行原函数,返回原函数的返回值立即执行原函数,返回原函数的返回值返回一个新函数,不立即执行
主要用途继承、调用对象方法数组操作(如求最大值)、合并数组回调函数、保持 this 指向、柯里化

3. 深度解析与代码示例

为了演示,我们先定义两个对象和一个通用函数:

const person = {
  name: "Alice",
  age: 25,
};

const dog = {
  name: "Buddy",
  age: 3,
};

function introduce(greeting, punctuation) {
  // 这里的 this 指向调用时的上下文
  console.log(
    `${greeting}, I am ${this.name}, ${this.age} years old${punctuation}`,
  );
}

3.1 Function.prototype.call()

call 方法接受一个 this 指向作为第一个参数,后面的参数逐个传入。

特点:参数列表形式,直观清晰。

// 将 introduce 函数的 this 指向 person 对象
introduce.call(person, "Hello", "!");
// 输出: Hello, I am Alice, 25 years old!

// 将 introduce 函数的 this 指向 dog 对象
introduce.call(dog, "Woof", ".");
// 输出: Woof, I am Buddy, 3 years old.

经典应用场景:实现继承

function Animal(name) {
  this.name = name;
}

function Cat(name, color) {
  // 借用 Animal 的构造函数,将 this 指向 Cat 的实例
  Animal.call(this, name);
  this.color = color;
}

const myCat = new Cat("Kitty", "white");
console.log(myCat.name); // 'Kitty'
console.log(myCat.color); // 'white'

3.2 Function.prototype.apply()

apply 方法与 call 非常相似,唯一的区别在于第二个参数必须是一个数组(或类数组对象)

特点:适合处理数组参数。

// 参数以数组形式传递
introduce.apply(person, ["Hi", "?"]);
// 输出: Hi, I am Alice, 25 years old?

经典应用场景:数学运算

在没有 ES6 展开运算符 (...) 之前,apply 常用于将数组传递给不接受数组参数的函数。

const numbers = [5, 6, 2, 3, 7];

// Math.max 不接受数组,只接受逐个参数
// 使用 apply 将数组展开为参数列表
const max = Math.max.apply(null, numbers);
console.log(max); // 7

// 注:现代 JS 更推荐写法: Math.max(...numbers)

3.3 Function.prototype.bind()

bind 方法不会立即执行函数,而是返回一个新的函数。这个新函数的 this 被永久绑定到了指定的对象上。

特点:返回新函数,可延迟执行,this 指向不可再次修改。

// 创建一个新函数,this 永久绑定到 person
const boundIntroduce = introduce.bind(person, "Hey");

// 稍后执行
boundIntroduce("!");
// 输出: Hey, I am Alice, 25 years old!

// 即使尝试再次改变 this,也不会生效
boundIntroduce.call(dog, "?");
// 输出依然是: Hey, I am Alice, 25 years old! (this 仍然是 person)

经典应用场景:setTimeout 中的 this 丢失

在事件回调或定时器中,this 往往会指向 windowundefined,使用 bind 可以锁定上下文。

const button = {
  label: "Click Me",
  clickHandler: function () {
    console.log(`Button ${this.label} clicked`);
  },
};

// 错误示范:this 指向 window
// setTimeout(button.clickHandler, 1000);

// 正确示范:绑定 this 到 button 对象
setTimeout(button.clickHandler.bind(button), 1000);
// 1秒后输出: Button Click Me clicked

4. 面试高频考点总结

call 和 apply 的区别?

bind 和 call/apply 的区别?

bind 返回的函数能再次被 bind 吗?

如果第一个参数传 null 或 undefined 会发生什么?

5. 手写简易版 Polyfill(进阶)

理解原理的最好方式就是自己实现一个。以下是 call 的简易实现思路:

Function.prototype.myCall = function (context, ...args) {
  // 1. 判断调用者是否为函数
  if (typeof this !== "function") {
    throw new TypeError("Error");
  }

  // 2. 处理 context,如果是 null/undefined 则指向 window/globalThis
  context = context || globalThis;

  // 3. 将当前函数挂载到 context 上,作为其方法
  // 使用 Symbol 避免属性名冲突
  const fnKey = Symbol("fn");
  context[fnKey] = this;

  // 4. 执行该函数,并保存结果
  const result = context[fnKey](...args);

  // 5. 删除临时添加的属性
  delete context[fnKey];

  // 6. 返回结果
  return result;
};

结语

callapplybind 是 JavaScript 灵活性的体现。掌握它们,不仅能让你更好地控制 this 指向,还能写出更优雅、复用性更高的代码。

希望这篇文章能帮你彻底攻克这三个方法!

以上就是一文详解JavaScript中 call、apply、bind的详细内容,更多关于JavaScript中call、apply、bind详解的资料请关注脚本之家其它相关文章!

您可能感兴趣的文章:
阅读全文