JavaScript 中 call、apply、bind 这三个方法的具体用法和区别
核心概念先理解
这三个方法都属于 Function.prototype 上的方法,所有函数都可以调用它们,核心作用是:改变函数执行时的 this 指向,但调用方式和效果有明显区别。
1. call 方法
用法
函数名.call(thisArg, arg1, arg2, ...)thisArg:函数执行时this要指向的对象(如果传 null/undefined,非严格模式下 this 指向 window/global)arg1, arg2...:传给函数的参数,逐个传递- 调用后立即执行函数
- 代码示例
-
// 定义一个基础函数 function sayHi(name, age) { console.log(`我是${name},年龄${age},当前this指向:`, this); } // 定义一个要绑定的对象 const person = { id: 1, name: "小明" }; // 用 call 改变 this 指向,同时传参 sayHi.call(person, "小红", 18); // 输出:我是小红,年龄18,当前this指向: {id: 1, name: '小明'} // 不传 thisArg(非严格模式) sayHi.call(null, "小刚", 20); // 输出:我是小刚,年龄20,当前this指向: window(浏览器环境)运行结果如下

2. apply 方法
用法
函数名.apply(thisArg, [arg1, arg2, ...])thisArg:和call一致,是 this 要指向的对象- 第二个参数是数组 / 类数组,里面的元素会作为参数传给函数
- 调用后立即执行函数
代码示例
// 定义一个基础函数
function sayHi(name, age) {
console.log(`我是${name},年龄${age},当前this指向:`, this);
}
// 定义一个要绑定的对象
const person = {
id: 1,
name: "小明"
};
// 复用上面的 sayHi 函数和 person 对象
sayHi.apply(person, ["小兰", 19]);
// 输出:我是小兰,年龄19,当前this指向: {id: 1, name: '小明'}
// 典型场景:求数组最大值(Math.max 本身不支持数组,用 apply 传参)
const nums = [1, 9, 5, 7];
const maxNum = Math.max.apply(null, nums);
console.log(maxNum); // 输出:9
运行结果如下

3. bind 方法
用法
const 新函数 = 函数名.bind(thisArg, arg1, arg2, ...)thisArg:和前两者一致arg1, arg2...:可选,提前给函数绑定的参数(柯里化)- 不会立即执行,而是返回一个永久绑定了 this 指向的新函数
核心特点
- 永久绑定
this
即使后续用call/apply也无法改变已绑定的this(除非使用new调用)。 - 支持参数预设(Partial Application)
可以提前传入一部分参数,剩下的在调用时再传。 - 不立即执行
与call/apply不同,bind只返回函数,需手动调用。
代码示例
// 定义一个基础函数
function sayHi(name, age) {
console.log(`我是${name},年龄${age},当前this指向:`, this);
}
// 定义一个要绑定的对象
const person = {
id: 1,
name: "小明"
};
// 复用 sayHi 函数和 person 对象
const bindSayHi = sayHi.bind(person, "小李");
// bind 返回新函数,未执行
// 执行新函数,补充剩余参数
bindSayHi(21);
// 输出:我是小李,年龄21,当前this指向: {id: 1, name: '小明'}
运行结果如下

下面扩展一下,看个有意思的代码:
const obj = {
name: "张三",
fn: function() {
console.log(this.name);
} // 绑定 this 为 obj
};
obj.fn();
运行结果如下

// 典型场景:解决定时器中 this 指向问题
const obj = {
name: "张三",
fn: function() {
setTimeout(function() {
console.log(this.name);
}, 1000); // 绑定 this 为 obj
}
};
obj.fn();
运行结果如下

添加了setTimeout函数后,输出结果是:undefined
这是为什么呢?
先拆解:为什么直接调用 obj.fn() 时 this 指向 obj?
obj.fn() 是对象方法调用,这是 JS 里的一种明确调用规则:
对象.函数名() 的形式调用时,函数内部的 this 会自动绑定到这个调用它的对象(也就是 obj)。代码执行逻辑:
const obj = {
name: "张三",
fn: function() {
console.log(this.name); // 调用时 this → obj
}
};
obj.fn(); // 「对象.方法」调用 → this 指向 obj → 输出 "张三"
关键:为什么 setTimeout 里的 this 不指向 obj?
setTimeout 的本质是「把函数作为参数传递,由 JS 引擎在指定时间后独立调用这个函数」,而非「以 obj 的方法形式调用」。我们把
setTimeout 的执行逻辑拆解成更易理解的伪代码,就能一眼看明白:// 模拟 setTimeout 的底层逻辑(简化版)
function setTimeout(callback, delay) {
// 1. 等待 delay 毫秒
// 2. 时间到后,JS 引擎「直接调用」回调函数,没有任何对象前缀
callback(); // 这里是「普通函数调用」,而非 obj.callback()
}
// 你的原代码等价于:
const obj = {
name: "张三",
fn: function() {
// 把一个匿名函数传给 setTimeout
const callback = function() {
console.log(this.name);
};
// JS 引擎后续会执行 callback() → 普通函数调用
setTimeout(callback, 1000);
}
};
obj.fn();
这里的核心规则是:
普通函数调用(没有任何对象前缀,直接写
函数名())时,this 指向全局对象(浏览器中是 window,Node.js 中是 global;严格模式下是 undefined)。正因为定时器里的回调函数是「普通函数调用」,而非「
obj.函数()」的形式,所以它的 this 不会指向 obj,而是指向全局对象(全局对象没有 name 属性,所以输出 undefined)。✅ 解决方案
方法 1:使用箭头函数(推荐)
箭头函数不绑定自己的
this,而是继承外层作用域的 this:const obj = {
name: "张三",
fn: function() {
setTimeout(() => {
console.log(this.name); // ✅ 正确输出 "张三"
}, 1000);
}
};
obj.fn();
✅ 简洁、清晰,是现代 JavaScript 的首选方式。
方法 2:使用 bind 显式绑定 this
const obj = {
name: "张三",
fn: function() {
setTimeout(function() {
console.log(this.name);
}.bind(this), 1000); // ✅ 绑定 this 到 obj
}
};
obj.fn();
✅ 兼容性好,适用于不支持箭头函数的旧环境。
方法 3:缓存 this(传统方法)
const obj = {
name: "张三",
fn: function() {
const self = this; // 缓存 this
setTimeout(function() {
console.log(self.name); // ✅ 使用缓存的引用
}, 1000);
}
};
obj.fn();
⚠️ 虽然有效,但不如箭头函数优雅,但易于理解。
以上三种解决方案都能正确地输出“张三”

浙公网安备 33010602011771号