JavaScript call、apply、bind 方法解析
JavaScript call、apply、bind 方法解析
在 JavaScript 中,call、apply、bind 都是用来**this** 改变函数执行时 指向 的核心方法,它们的核心目标一致,但使用方式、执行时机和传参形式有明显区别。
const dog = {
name: "旺财",
sayName() {
console.log(this.name);
},
eat(food) {
console.log(`${this.name} 在吃${food}`);
},
eats(food1, food2) {
console.log(`${this.name} 在吃${food1}和${food2}`);
},
};
const cat = {
name: "咪咪",
};
// call 会立即执行函数,并且改变 this 指向
dog.sayName.call(cat); // 输出 '咪咪'
dog.eat.call(cat, "🐟"); // 输出 '咪咪 在吃🐟'
dog.sayName.apply(cat); // 输出 '咪咪'
dog.eats.call(cat, "🐟", "🐔"); // 输出 '咪咪 在吃🐟和🐔'
dog.eats.apply(cat, ["🐟", "🐔"]); // 输出 '咪咪 在吃🐟和🐔'
const boundEats = dog.eats.bind(cat);
boundEats("🐟", "🐔"); // 输出 '咪咪 在吃🐟和🐔'
一、核心共性
三者的核心作用:this 手动指定函数执行时的 指向,突破函数默认的 this 绑定规则(比如对象方法的 this 原本指向对象本身,通过这三个方法可以强制指向其他对象)。
以示例中的 dog.sayName() 为例,默认执行时 this 指向 dog,但通过 call/apply/bind 可以让 this 指向 cat,从而输出 咪咪 而非 旺财。
二、逐个解析
1. call
-
执行时机:立即执行 函数
-
传参方式:第一个参数是
this要指向的目标对象,后续参数逐个单独传递(逗号分隔) -
语法:
函数.call(thisArg, arg1, arg2, ...)
示例解析:
// this 指向 cat,无额外参数,立即执行 sayName
dog.sayName.call(cat); // 输出 '咪咪'
// this 指向 cat,额外参数 '🐟' 逐个传递,立即执行 eat
dog.eat.call(cat, "🐟"); // 输出 '咪咪 在吃🐟'
// 多参数场景:参数逐个传递,立即执行 eats
dog.eats.call(cat, "🐟", "🐔"); // 输出 '咪咪 在吃🐟和🐔'
2. apply
-
执行时机:立即执行 函数(和
call一致) -
传参方式:第一个参数是
this要指向的目标对象,后续参数必须放在一个数组(或类数组)中传递 -
语法:
函数.apply(thisArg, [arg1, arg2, ...])
示例解析:
// 无额外参数,数组可以为空(或不传),立即执行 sayName
dog.sayName.apply(cat); // 输出 '咪咪'
// 多参数场景:参数放在数组中传递,立即执行 eats
dog.eats.apply(cat, ["🐟", "🐔"]); // 输出 '咪咪 在吃🐟和🐔'
注意:apply 适合参数数量不固定、或参数已存在于数组中的场景(比如 Math.max.apply(null, [1,2,3]) 求数组最大值)。
3. bind
-
执行时机:不立即执行 函数,而是返回一个绑定了新 this 指向的新函数,后续需要手动调用这个新函数才会执行
-
传参方式:第一个参数是
this要指向的目标对象,后续参数可以提前绑定(柯里化),也可以在调用新函数时补充 -
语法:
const 新函数 = 函数.bind(thisArg, arg1, arg2, ...); 新函数(剩余参数);
示例解析:
// 第一步:bind 不执行,仅绑定 this 为 cat,返回新函数 boundEats(原变量名 boundSayName 已修改)
const boundEats = dog.eats.bind(cat);
// 第二步:手动调用新函数,传递参数 '🐟' 和 '🐔',此时才执行 eats
boundEats("🐟", "🐔"); // 输出 '咪咪 在吃🐟和🐔'
进阶用法:
// 提前绑定部分参数(柯里化),this 仍指向 cat
const boundEatWithFish = dog.eats.bind(cat, "🐟");
// 调用时补充剩余参数,同样输出目标结果
boundEatWithFish("🐔"); // 输出 '咪咪 在吃🐟和🐔'
三、核心区别总结
| 特性 | call | apply | bind |
|---|---|---|---|
| 执行时机 | 立即执行 | 立即执行 | 不立即执行,返回新函数 |
| 传参形式 | 逐个传递(逗号分隔) | 数组/类数组传递 | 可提前绑定,也可调用时传 |
| 返回值 | 函数执行结果 | 函数执行结果 | 绑定 this 后的新函数 |
四、常见使用场景
-
call:适用于参数数量明确、需要立即执行的场景(比如继承:
Parent.call(this, arg1)); -
apply:适用于参数是数组/类数组的场景(比如求数组最大值:
Math.max.apply(null, arr)); -
bind:适用于需要延迟执行、或需要重复使用绑定 this 后的函数的场景(比如事件回调、定时器:
btn.onclick = fn.bind(obj))。
五、补充注意点
-
如果第一个参数传
null/undefined,在非严格模式下,this会指向全局对象(浏览器中是window,Node 中是global);严格模式下this为null/undefined。 -
bind返回的新函数不能通过call/apply再次修改this指向(bind的绑定是永久的)。