14. bind、call、apply的底层原理 副本.md 来源:https://my.feishu.cn/file/ZDTdbB5pioA79UxNNyCcWLU1n8b 采集状态:已完成正文采集;原文件源码视图逐行采集,行号 1 至 99 连续,滚动到底。 ## 1. 核心概念 (Core Concept) `call`、`apply` 和 `bind` 是 JavaScript 中 Function.prototype 对象上的三个方法,主要用于**控制函数执行时的上下文(即 `this` 的指向)**。它们允许开发者显式地指定函数内部 `this` 关键字的值。 ## 2. 为什么需要它? (The "Why") 1. **解决 `this` 指向的动态性/不确定性:** 在 JavaScript 中,`this` 的值取决于函数被调用的方式(全局调用、对象方法调用、构造函数调用、事件处理函数等)。这可能导致在某些场景下(如异步回调、脱离对象调用的方法) `this` 不是我们期望的对象。`call`、`apply` 和 `bind` 提供了一种方式来强制指定 `this`。 2. **借用其他对象的方法:** 可以方便地调用一个对象上的方法,同时让该方法的 `this` 指向另一个对象,实现方法的复用。 3. **函数参数的处理:** `call` 和 `apply` 在传递函数参数的方式上略有不同,提供了灵活的参数传递机制。 ## 3. API 与用法 (API & Usage) 这三个方法都定义在 `Function.prototype` 上,因此所有函数实例都可以调用这些方法。 ### 3.1 `call(thisArg, arg1, arg2, ...)` * **作用:** 调用一个函数,并显式指定该函数内部 `this` 的值,以及按顺序传递参数。 * **参数:** * `thisArg`: 在 fun 函数运行时指定的 `this` 值。如果 `thisArg` 为 `null`、`undefined`,或者是在非严格模式下,`thisArg` 会被自动替换为全局对象 (`window` 或 `global`)。基本类型值会被包装成对应的包装对象。 * `arg1, arg2, ...`: 传入函数的参数序列。 * **返回值:** 函数的返回值。 ```javascript function greet(greeting, punctuation) { console.log(`${greeting}, ${this.name}${punctuation}`); } const person = { name: "Alice" }; // 使用 call 调用 greet,指定 this 为 person,并按顺序传递参数 greet.call(person, "Hello", "!"); // 输出: Hello, Alice! ``` ### 3.2 `apply(thisArg, argsArray)` * **作用:** 调用一个函数,显式指定 `this` 的值,并通过**一个数组**来传递参数。 * **参数:** * `thisArg`: 同 `call`。 * `argsArray`: 一个数组或类数组对象,其元素作为单独的参数传递给函数。 * **返回值:** 函数的返回值。 ```javascript function sum(a, b, c) { console.log(this.name, a + b + c); } const obj = { name: "Calculator" }; const numbers = [1, 2, 3]; // 使用 apply 调用 sum,指定 this 为 obj,通过数组传递参数 sum.apply(obj, numbers); // 输出: Calculator 6 ``` ### 3.3 `bind(thisArg, arg1, arg2, ...)` * **作用:** 创建一个新的函数,当这个新函数被调用时,其 `this` 值会被绑定到 `thisArg`,参数会被预设为从第二个参数开始的值。`bind` **不会立即执行函数**。 * **参数:** * `thisArg`: 作为绑定函数的 `this` 对象。 * `arg1, arg2, ...`: 当绑定函数被调用时,这些参数将作为预设参数放在最前面传递给原始函数。 * **返回值:** 一个新的、原函数的拷贝,其 `this` 已被绑定,并可能预设了部分参数。 ```javascript function sayHello(greeting) { console.log(`${greeting}, ${this.name}`); } const user = { name: "Bob" }; // 使用 bind 创建一个新的函数 boundSayHello const boundSayHello = sayHello.bind(user, "Hi"); // 调用新函数,this 已固定为 user,"Hi" 是预设参数 boundSayHello(); // 输出: Hi, Bob ``` ## 4. 关键注意事项 (Key Considerations) 1. **`call` 和 `apply` 的主要区别:** 仅在于传递参数的方式:`call` 接收参数序列,`apply` 接收参数数组。在性能上,通常两者差异微小,但在参数数量极大时,某些引擎中 apply 可能稍有优势(但不是通用规则)。 2. **`bind` 的非立即执行特性:** `bind` 返回的是一个新函数,原函数并不会立即执行。这使得 `bind` 特别适用于需要延迟执行或作为回调函数,同时又需要固定 `this` 的场景(如事件监听、异步处理)。 3. **绑定后的函数作为构造函数:** 通过 `bind` 创建的新函数,如果在 `new` 操作符下调用,`this` 的指向会变为新创建的实例对象,而不是 `bind` 时指定的 `thisArg`。但是,`bind` 时指定的参数仍会传递。 4. **模拟实现:** 理解 `call`、`apply`、`bind` 的底层原理,可以通过在 `Function.prototype` 上模拟实现它们来加深理解。核心思路通常是: * 将需要调用(或绑定)的函数临时作为指定 `thisArg` 对象的一个方法。 * 通过该对象去调用这个方法,从而利用**点语法调用**时 `this` 会指向调用者的特性。 * 在调用后删除临时添加的方法。 * 处理参数的传递(序列或数组)。 * `bind` 的模拟需要返回一个新的函数,并在调用时执行上述步骤,同时处理好参数的合并(绑定时传入的参数 + 调用时传入的参数)。 ## 5. 参考资料 (References) * **MDN Web Docs:** * [`Function.prototype.call()`](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Function/call) * [`Function.prototype.apply()`](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Function/apply) * [`Function.prototype.bind()`](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Function/bind) * **JavaScript 规范 (ECMAScript):** 虽然规范不直接描述底层实现,但定义了这些方法的行为。 * [ECMAScript Language Specification - Function Objects](https://tc39.es/ecma262/) (需要查阅对应版本的规范,搜索 `call`, `apply`, `bind`) * **业界技术博客/书籍 (模拟实现):** * 许多高质量的技术博客或 JavaScript 原理书籍会包含 `call`, `apply`, `bind` 的模拟实现,以此来解释其工作原理。例如,许多讲解 JavaScript 深入原理的博客文章。**(注意:此处不列举具体博客链接,以保持信源的通用性和权威性偏好,但这类资源是理解模拟实现的常见途径)** 原文链接与附件索引 `Function.prototype.call()` https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Function/call `Function.prototype.apply()` https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Function/apply `Function.prototype.bind()` https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Function/bind ECMAScript Language Specification - Function Objects https://tc39.es/ecma262/