JavaScript中apply、call与bind的核心用法与区别详解
本文深入解析JavaScript中apply、call和bind三个方法的用法。它们均用于改变函数执行时的this指向,但存在参数传递方式、返回值及执行时机的差异。文章通过数组追加、获取极值、伪数组转换及事件处理等实例,详细对比了三者的适用场景与实现原理,帮助开发者掌握动态上下文切换的技巧。
本文旨在清晰阐述JavaScript中apply、call和bind的核心概念,并通过列举实际应用场景来加深理解与记忆。
apply与call基础
apply与call基础
在JavaScript中,call和apply的存在意义在于改变函数运行时的上下文(context),即动态调整函数内部this的指向。
JavaScript的一个显著特性是函数拥有「定义时上下文」和「运行时上下文」,且运行时上下文是可以被改变的。
首先通过一段代码示例进行说明:
function fruits() {}
fruits.prototype = {
color: "red",
say: function() {
console.log("My color is " + this.color);
}
}
var apple = new fruits;
apple.say(); //My color is red
假设存在对象banana = {color : "yellow"},若不想为其重新定义say方法,可以通过call或apply借用apple对象的say方法:
banana = {
color: "yellow"
}
apple.say.call(banana); //My color is yellow
apple.say.apply(banana); //My color is yellow
由此可见,call和apply用于动态改变this指向。当某个对象(如banana)缺少某方法,而其他对象(如apple)拥有该方法时,可借助call或apply调用其他对象的方法进行操作。
apply与call的区别
apply与call的区别
apply和call的功能完全一致,主要区别在于接收参数的方式不同。例如定义如下函数:
var func = function(arg1, arg2) {
};
可通过以下方式调用:
func.call(this, arg1, arg2);
func.apply(this, [arg1, arg2])
其中this指代想要指定的上下文,可以是任何JavaScript对象。call要求参数按顺序逐个传递,而apply则将参数封装在数组中传递。
若参数数量明确已知,使用call更为合适。
若参数数量不确定,则使用apply,并将参数push进数组后传递。函数内部也可通过arguments数组遍历所有参数。
为巩固记忆,以下列举常见用法:
常见应用场景
1、数组之间追加
var array1 = [12 , "foo" , {name "Joe"} , -2458];
var array2 = ["Doe" , 555 , 100];
Array.prototype.push.apply(array1, array2);
/* array1 值为 [12 , "foo" , {name "Joe"} , -2458 , "Doe" , 555 , 100] */
2、获取数组中的最大值和最小值
var numbers = [5, 458 , 120 , -215 ];
var maxInNumbers = Math.max.apply(Math, numbers), //458
maxInNumbers = Math.max.call(Math,5, 458 , 120 , -215); //458
Number类型本身没有max方法,但Math对象有,因此可借助call或apply调用其方法。
3、验证对象是否为数组(前提:toString()方法未被重写)
functionisArray(obj){
return Object.prototype.toString.call(obj) === '[object Array]' ;
}
4、类(伪)数组使用数组方法
var domNodes = Array.prototype.slice.call(document.getElementsByTagName("*"));
JavaScript中存在一种名为伪数组的对象结构。例如arguments对象,以及getElementsByTagName、document.childNodes等方法返回的NodeList对象,均属于伪数组,无法直接应用Array下的push、pop等方法。
但可通过Array.prototype.slice.call将其转换为具有length属性的真数组,从而使domNodes能够使用Array下的所有方法。
深入理解apply与call的应用
深入理解apply与call的应用
接下来通过一道面试题,更深入地理解apply和call。
定义一个log方法,用于代理console.log方法,常见解法如下:
function log(msg) {
console.log(msg);
}
log(1); //1
log(1,2); //1
上述方法可满足基本需求,但当传入参数个数不确定时则失效。此时应考虑使用apply或call,由于参数数量不确定,使用apply是最佳选择,方法如下:
function log(){
console.log.apply(console, arguments);
};
log(1); //1
log(1,2); //1 2
进一步要求为每个log消息添加"(app)"前缀,例如:
log("hello world"); //(app)hello world
如何优雅实现?需意识到arguments是伪数组,通过Array.prototype.slice.call转化为标准数组,再使用unshift方法,如下所示:
function log(){
var args = Array.prototype.slice.call(arguments);
args.unshift('(app)');
console.log.apply(console, args);
};
bind方法
bind方法
介绍完apply和call后,讨论bind。bind()方法与apply和call类似,同样用于改变函数体内this的指向。
MDN解释指出:bind()方法会创建一个新函数,称为绑定函数。调用该绑定函数时,其this为创建时传入bind()的第一个参数;后续参数加上原函数运行时的参数按顺序作为原函数的参数来调用。
直接查看具体用法。在常见的单体模式中,通常使用_this、that或self保存this,以便在上下文改变后继续引用。例如:
var foo = {
bar : 1,
eventBind: function(){
var _this = this;
$('.someClass').on('click',function(event) {
/* Act on the event */
console.log(_this.bar); //1
});
}
}
由于JavaScript的机制,上下文环境在eventBind:function(){}过渡到$('.someClass').on('click',function(event){})时发生改变,上述使用变量保存this的方式有效且无问题。当然,使用bind()可更优雅地解决:
var foo = {
bar : 1,
eventBind: function(){
$('.someClass').on('click',function(event) {
/* Act on the event */
console.log(this.bar); //1
}.bind(this));
}
}
在上述代码中,bind()创建了一个函数。当该click事件绑定时,其this关键词被设置为传入bind()的参数。因此,这里传入期望的上下文this(即foo)到bind()中。当回调函数执行时,this便指向foo对象。再举一个简单的例子:
var bar = function(){
console.log(this.x);
}
var foo = {
x:3
}
bar(); // undefined
var func = bar.bind(foo);
func(); // 3
这里创建了新函数func。使用bind()创建绑定函数后,执行时其this被设置为foo,而非调用bar()时的全局作用域。
有一个有趣的问题:如果连续bind()两次,或连续bind()三次,输出值是什么?例如:
var bar = function(){
console.log(this.x);
}
var foo = {
x:3
}
var sed = {
x:4
}
var func = bar.bind(foo).bind(sed);
func(); //?
var fiv = {
x:5
}
var func = bar.bind(foo).bind(sed).bind(fiv);
func(); //?
答案是,两次调用均输出3,而非预期的4和5。原因是,在JavaScript中,多次bind()是无效的。深层原因在于,bind()的实现相当于在内部封装了call/apply,第二次bind()相当于再次封装第一次bind(),因此第二次及后续的bind()无法生效。
apply、call与bind的比较
apply、call与bind的比较
那么,apply、call和bind三者相比,有何异同?何时使用apply或call,何时使用bind?一个简单的例子:
var obj = {
x: 81,
};
var foo = {
getX: function() {
return this.x;
}
}
console.log(foo.getX.bind(obj)()); //81
console.log(foo.getX.call(obj)); //81
console.log(foo.getX.apply(obj)); //81
三个输出均为81,但注意使用bind()的写法后多了对括号。
区别在于:若希望改变上下文环境后并非立即执行,而是回调执行时,使用bind()方法;而apply/call则会立即执行函数。
再总结一下:
apply、call、bind三者均用于改变函数this对象的指向; apply、call、bind三者的第一个参数均为this要指向的对象,即指定的上下文; apply、call、bind三者均可利用后续参数传参;
bind返回对应函数,便于稍后调用;apply、call则是立即调用。
好了,本文关于JavaScript中apply、call和bind用法的相关教程到此结束,希望对大家有所帮助。
JavaScript入门书籍避坑:这些书不适合初学者
2026-11-05 16:31:15
JS使用Math.round实现保留两位小数的原理与代码
2026-11-05 15:35:55
JavaScript 原生 Date 对象怎么实现日期加减
2026-11-05 14:30:00
JavaScript函数定义的三种方式:声明、表达式与箭头函数
2026-11-05 13:50:55
JavaScript中escape、encodeURI和encodeURIComponent对空格的处理差异
2026-11-05 12:47:23
