🔹 1. 共同点
-
都定义在 Function.prototype 上。
-
都可以指定函数运行时的 this。
-
都支持传参。
换句话说,它们都是为了 改变函数调用时的上下文 而设计的。
🔹 2. 区别对比
| 方法 | 语法 | 参数形式 | 是否立即执行 | 返回值 |
|---|---|---|---|---|
| call | fn.call(thisArg, arg1, arg2, …) | 参数列表(逐个传) | ✅ 是 | 函数执行结果 |
| apply | fn.apply(thisArg, [argsArray]) | 数组或类数组(一次性传入) | ✅ 是 | 函数执行结果 |
| bind | fn.bind(thisArg, arg1, arg2, …) | 参数列表(可预设一部分) | ❌ 否 | 返回新函数(this 已绑定) |
🔹 3. 示例代码
function greet(greeting, punctuation) {
console.log(`${greeting}, ${this.name}${punctuation}`);
}
const user = { name: "Alice" };
// call:参数逐个传
greet.call(user, "Hello", "!");
// => Hello, Alice!
// apply:参数打包成数组
greet.apply(user, ["Hi", "!!!"]);
// => Hi, Alice!!!
// bind:不会立即执行,返回一个新函数
const bound = greet.bind(user, "Hey");
bound("?");
// => Hey, Alice?
🔹 4. bind 的特殊点
-
惰性执行:不会立即调用,必须手动执行返回的新函数。
-
参数柯里化:第一次绑定时可传一部分参数,调用时继续传。
-
构造函数场景:用 new 调用时,this 会指向新对象,忽略 bind 的 this。
function Person(name) {
this.name = name;
}
const BoundPerson = Person.bind({ x: 1 });
const p = new BoundPerson("Alice");
console.log(p.name); // Alice
这里 new 的优先级更高,覆盖了 bind 绑定的 this。
🔹 5. 记忆技巧
-
call:C → Comma(逗号传参,一个个列出来)。
-
apply:A → Array(数组传参)。
-
bind:B → Bind & return(绑定并返回新函数,不执行)。
🔹 6. 历史背景:为什么有 call 和 apply?
很多人会疑惑:call 和 apply 区别不大,为什么要设计两个方法?
(1)ES3 时代的限制
1999 年 ES3 发布时:
-
没有扩展运算符 ...。
-
没有解构语法。
-
甚至还没有 bind。
如果要把数组作为参数传给函数,写法非常笨拙:
function sum(a, b, c) {
return a + b + c;
}
const arr = [1, 2, 3];
// 没有 ...arr 只能手动展开
sum(arr[0], arr[1], arr[2]);
为了解决这个问题,apply 应运而生:
sum.apply(null, [1, 2, 3]); // ✅ 直接传数组
而 call 则是参数列表形式的补充:
sum.call(null, 1, 2, 3);
两者语义清晰,互为补充。
(2)ES5 之后的新变化
2009 年 ES5 引入了 bind,可以返回一个预绑定 this 的函数。
2015 年 ES6 增加了扩展运算符 ...,于是以前只能用 apply 的写法,现在可以用 call 替代:
// 以前必须 apply
Math.max.apply(null, [1, 2, 3]);
// ES6 之后
Math.max.call(null, ...[1, 2, 3]);
因此在现代语法中,apply 的存在感逐渐降低。
(3)为什么仍然保留 apply?
-
历史包袱:大量旧代码依赖 apply,无法移除。
-
语义清晰:一眼看到 apply,就知道参数是数组。
-
规范稳定性:JavaScript 倡导“不废弃 API”,保持向后兼容。
🔹 7. 总结
-
call 和 apply 功能几乎一样,唯一区别是参数传递形式。
-
在 ES3/ES5 时代,这种设计很有必要,因为没有展开语法。
-
ES6 之后 apply 的存在感降低,但仍保留作为历史兼容和语义工具。
-
bind 则是更“现代”的 API,解决惰性执行和函数柯里化的问题。
👉 可以说,call 与 apply 是 JavaScript 早期语法限制下的“双胞胎产物”,而 bind 是后期补充的新成员。