JS 中 bind、call、apply 超清晰讲解(新手一看就懂)
你想彻底搞懂 JS 里 bind、call、apply 这三个高频面试点+开发常用方法,我用最通俗的语言 + 核心原理 + 极简示例 + 区别总结,一次性讲透,不绕弯子。
一、先搞懂:它们到底是干嘛的?
一句话核心:
这三个方法,都是用来强行改变函数中 this 的指向!
JS 里 this 指向很灵活,谁调用函数,this 就指向谁。
但有时候我们想手动指定 this 是谁,就用 call / apply / bind。
它们都是 Function 原型上的方法,所有函数都能直接用。
二、先看最简单的:call(立即执行)
语法
函数.call(新的this指向, 参数1, 参数2, 参数3...)
特点
- 立即调用函数
- 第一个参数是
this要指向的对象 - 后面的参数一个一个传
示例
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...])
特点
- 立即调用函数
- 第一个参数 =
this指向 - 第二个参数必须是数组/类数组
示例
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...)
特点
- 不会立即执行
- 返回一个永久绑定 this 的新函数
- 参数和 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);
}
};
七、最简单的总结(背会就够用)
- call、apply、bind 都能改 this
- call / apply 马上运行,bind 返回函数稍后运行
- call 逐个传参,apply 数组传参
- 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>