本文深入解析JavaScript中call、apply和bind三个核心函数的用法与区别。通过代码示例说明call和apply如何借用对象方法并改变this指向,重点对比两者在参数传递方式上的差异(逐个列出与数组包装)。同时介绍bind方法如何实现函数柯里化及事件绑定中的上下文保留,并总结三者的兼容性与适用场景。
引言:JavaScript核心函数概述
在JavaScript开发中,call、apply和bind是必须掌握的核心知识点。本文将详细总结这三个函数的具体用法及其常见应用场景。
call函数的原理与应用
首先分析call函数,其核心概念可以理解为“借用”或“请求”。设想这样一个场景:你独自在外遇到急事需要打电话回家,但手机因欠费、没电或损坏无法使用。此时,你可以借用朋友、邻居或公用电话完成通话,至于使用谁的电话并不重要,最终效果与使用自己的手机一致。call函数的设计初衷与此类似,旨在借用其他对象的方法来实现当前需求。以下代码模拟了这一应用场景:
var frog = {
name : 'frog',
say : function(){
alert(this.name);
}
}
var rabbit = {
name : 'rabbit'
}
frog.say.call(rubbit) // rabbit
假设rubbit对象是一个无法发声的哑巴,它希望输出自己的名字,但仅靠自身能力无法实现。幸运的是,它有一个名为frog的朋友具备说话能力。于是,rubbit请求frog代为执行say方法。在调用frog.say.call()时,第一个参数必须指定“委托人”,即方法执行时this指向的对象。这里rubbit请求frog帮忙,因此传入rubbit。这样,say方法执行时会查找rubbit的name属性,而非frog的name。如果传入frog,则相当于自己执行自己的方法,例如:
var frog = {
name : 'frog',
say : function(){
alert(this.name);
}
}
var rabbit = {
name : 'rabbit'
}
frog.say.call(frog) // frog
显然,自己执行自己的方法会输出自己的名字,这符合预期。接下来我们看call的经典用法:
//把参数转化成真正的数组对象
function frog(){
var arr = [].slice.call(arguments);
console.log(arguments.slice,arr.slice)
// undefined function slice() { [native code] }
}
frog(1,2,3,4)
通过call方法,我们可以将arguments对象转换为真正的数组对象,从而使用数组方法。在jQuery源码中,这种用法非常常见。回到之前的比喻,借电话只是基础操作,打完电话后可能还需要捎带物品回家。考虑到现代生活压力大,请假不仅扣薪还要花费路费,性价比极低。此时,call函数可以帮忙“顺道携带”物品,且无需额外费用、不限数量或重量。以下代码演示了这一过程:
var frog = {
name : 'frog',
send : function(money,food,milk,suagate){
alert(money+food+milk+suagate);
}
}
var rabbit = {
name : 'rabbit'
}
frog.send.call(rubbit,'money','food','milk','suagate')
如果预算充足,甚至可以寄送iPhone 6 Plus等贵重物品回家。
至此,call函数的讲解基本结束。希望上述情景类比能帮助你理解call的工作原理。如果引发了你的思乡之情,对此表示歉意。
apply函数的参数传递差异
call还有一个“同父异母”的兄弟,名为apply。如果理解了call,apply的原理也类似,主要区别在于参数传递方式:apply不喜欢逐个传递参数,而是提供一个“大箱子”(即数组)来包装所有参数。以上述例子使用apply实现如下:
var frog = {
name : 'frog',
send : function(money,food,milk,suagate){
alert(money+food+milk+suagate);
}
}
var rabbit = {
name : 'rabbit'
}
//注意参数的区别
frog.send.apply(rubbit,['money','food','milk','suagate'])
bind函数的特性与兼容性
以上就是call和apply的核心用法。此外,它们的“父亲”(即Function原型)早年还生有一个名为bind的“私生子”。虽然bind比call和apply晚出现几年,但能力不容小觑。需要注意的是,bind在部分旧版浏览器(如IE6)中不受支持。以下代码演示其功能:
var name = 'rubbit';
var frog = {
name : 'frog',
say : function(){
setTimeout(function(money,milk){
alert(this.name+money+milk)
}.bind(this,'money','milk'),1000)
}
}
frog.say();
对比发现,bind可以直接链接在函数定义之后使用,相当于省略了call和apply的步骤,直接在函数后指定委托人和传递参数。从传参风格来看,bind更接近call。
再看一段bind的经典用法:
var obj = {
name : 'frog'
}
document.addEventListener('click',function(){
alert(this.name); // frog
}.bind(obj),false);
三者对比总结
总结来说,apply、call和bind这三个函数的相同点在于:
1. 第一个参数均用于绑定作用域,即指定方法执行时的this指向。
2. 三者均支持传递参数。
不同点在于:
apply和call具有更好的浏览器兼容性,而bind在部分低版本浏览器中不支持。
apply要求传递的参数必须封装在数组中,而call和bind则是将参数逐一列出。
希望本文能帮助你更深入地理解call、apply和bind三个函数的用法,对JavaScript开发有所助益。