本文详细解析JavaScript中call与apply方法的用法。重点讲解thisArg参数对this指向的影响,包括非严格模式下null/undefined指向全局对象的特性。通过代码示例对比两者在参数传递形式上的差异:call接受参数列表,apply接受数组。最后演示如何利用call实现对象间的属性继承。
call与apply方法概述
首先参考MDN文档中关于call方法的官方解释。
call()方法允许在指定thisArg和若干指定参数的前提下,调用一个函数或方法。
需要注意的是,call()与apply()功能相似,主要区别在于参数传递方式:call()接受的是若干个参数的列表,而apply()接受的是一个包含多个参数的数组。
语法与参数详解
语法格式如下。
fun.call(thisArg[, arg1[, arg2[, ...]]])
参数说明:thisArg。
这是fun函数运行时指定的this值。需注意的是,指定的thisArg未必是函数执行时实际的this值。若函数处于非严格模式,当thisArg为null或undefined时,它会自动指向全局对象(浏览器中为window);若thisArg为原始值(数字、字符串或布尔值),则指向该原始值的自动包装对象。
参数列表:arg1, arg2, ...
这些是传递给函数的具体参数列表。
MDN提供的初始示例可能较难理解,此处将其列出,感兴趣者可查阅call-Javascript相关文档。
this指向机制分析
上述thisArg被解释为fun运行时的this值,这意味着使用call后,fun内部的this将指向thisArg?请参考以下代码验证。
var p="456";
function f1(){
this.p="123";
}
function f2() {
console.log(this.p);
}
f2(); //456 f2.call(f1()); //123 f2.apply(f1()); //123
第一次输出的是全局变量,随后由于调用了call和apply,f2中的this指向了f1,因此输出变为123。这实际上表明f1借用了f2的方法来输出其自身的p属性。
若将f1()中的this.p删除,输出将变为三个456,这证实了当this为null或undefined时,确实指向了全局变量。
关于指向原始值即指向其包装对象的说明:由于作者理解中的包装对象均为临时存在,且测试时仅输出原始值的类型而非object类型,如何证明这一点若有了解的朋友欢迎讨论,谢谢!
利用call实现继承
既然call可以实现一个对象借用另一个对象的方法,那是否可以实现继承?请看代码示例。
function f1(){
this.father="father"
}
function f2() {
f1.call(this);
this.child="child";
} var test=new f2();
console.log(test.father); //father
test对象中原本没有father属性,这是因为在f2()中执行了以下代码。
f1.call(this);
此处的this指向的是f2,即f2借用了f1的方法,从而实现了继承效果。
参数传递差异对比
接下来讲解参数传递部分,这些参数是传递给fun的,具体见代码。
function f1(){
this.p="123";
}
function f2(x) {
console.log(this.p);
console.log(x);
}
f2.call(f1(),456); //123
//456
首先输出123是因为f1中的p属性,随后的456是传递给f2的参数,这很容易理解。
主要需注意call和apply在参数处理上的区别。
call是一个一个传入参数,而apply是传入一个数组。
function f1(){
this.p="测试call";
}
function f2(x,y,z) {
console.log(this.p);
console.log(x);
console.log(y);
console.log(z);
}
function f3(){
this.p="测试apply";
}
f2.call(f1(),4,5,6);
f2.call(f1(),[4,5,6]);
f2.apply(f3(),[4,5,6]);
f2.apply(f3(),4,5,6);
这里可以看到具体的运行结果。

第一段测试中,call正确输出了结果。
第二段测试中,由于传入的是数组,导致先输出一个数组,随后两个参数显示为undefined。
第三段测试中,apply正确输出了结果。
第四段测试中,由于参数格式错误直接报错。
这种区别应该非常明显。