JavaScript引用类型与call/apply作用域详解
本文解析JavaScript中Object和Array等引用类型的创建方式,对比new Object、字面量及对象直接量的区别。深入讲解Function类型的call与apply方法,通过实例演示如何利用它们扩展函数运行作用域,解析this指针指向与window对象的关系,帮助开发者理解作用域链及函数上下文切换机制。
JavaScript引用类型概述
引用类型
在JavaScript中,引用类型涵盖了Object、Array、Date、RegExp以及Function等多种类型。
使用引用类型时,必须基于该类型实例化出一个具体对象。可以将引用类型视为一种模板,通过该模板生成具体的对象实例进行使用,因此引用类型也常被称为对象定义。
例如,若要定义包含个人信息及行为方法的person对象,需依赖Object类型来实现:
var person = new Object();
person.name = "jiangshui";
person.sayName = function(){
console.log(this.name);
}
上述person对象利用new操作符基于Object模板创建。随后,可为该对象添加name属性及sayName方法。这些属性和方法是Object类型所具备的功能,通过引用类型创建的对象即可调用这些功能。
创建对象并非必须使用new操作符,部分类型支持简化创建方式。例如创建前述Object类型对象,还可采用以下两种方法:
var person = {};
person.name = "jiangshui";
person.sayName = function(){
console.log(this.name);
}
或
var person = {
name : "jiangshui",
sayName : function(){
console.log(this.name);
}
};
{}操作符的作用等同于new Object(),简化了代码书写。上述两种写法存在差异:第一种属于“追加”模式,即在已有定义基础上继续添加属性或方法,若存在同名属性或方法则会被覆盖;第二种属于“取代”模式,无论person对象之前是否定义了属性或方法,新定义的内容将完全替换之前的内容。由于引用类型生成的对象存储在内存区域,变量person仅保存指向该区域的指针,第二种写法实际上生成了一个新对象(新内存区域),并将person变量重新指向该新区域,从而取代了旧对象。理解这一机制对后续内容至关重要。
其他引用类型的用法大致相同,例如Array类型也可通过[]生成对象或直接定义。生成数组对象后,可按数组格式存储信息,同时对象可获得Array类型中定义的方法,如push、shift、sort等,并可调用这些方法,例如:
var colors = [];
colors.push('red','green');
console.log(colors);
上述代码通过Array类型创建数组对象,调用其中预定义的push方法向对象添加red和green两个值,最后在控制台打印输出即可查看结果。
call和apply方法的作用域扩展
call 和 apply 方法
这两个方法由Function类型提供,即可以在函数上调用。call和apply的功能相同,旨在扩充函数运行的作用域,区别在于使用call时,传递给函数的参数必须逐个列出,而apply方法则无需如此。可根据函数需求选择使用call或apply。
扩充函数运行作用域的具体含义是什么?通过示例即可理解。
可将函数理解为包裹在一个容器(即作用域)中,该容器内存在某些变量或其他元素。当函数运行时,若需调用这些变量,会在当前容器内查找。该容器外部还包裹着更大的容器,若当前小容器内未找到,函数会向更大的容器搜寻,依此类推,直至找到最大的window对象。但若函数在当前小容器内运行,且小容器内有对应变量,即便大容器中也存在同名变量,函数仍会调用小容器内的变量。
call和apply方法旨在解决此问题,突破容器限制。以前面例子为例:
var person = {
name : "jiangshui",
sayName : function(){
console.log(this.name);
}
};
在Chrome控制台粘贴并执行后,再执行person.sayName(),可以看到:

此时,person作为一个容器,其中创建了sayName方法(函数)。执行时,必须在person作用域下运行。若在最下方直接执行,即在window作用域下执行,会报错not defined,因为window下未定义sayName方法。其中的this指针较为特殊,它指向当前作用域,this.name即调用当前作用域下的name值。
接下来为window对象添加一个name属性:
window.name = "yujiangshui";
或直接
name = "yujiangshui";
由于window是最大的容器,因此可省略window,所有定义的属性或变量均挂靠到window上,验证如下:

若希望在window这个大容器下运行person小容器内的sayName方法,需使用call或apply来扩充sayName方法的作用域。执行以下语句:
person.sayName.call(window);
或者
person.sayName.call(this);
输出结果一致,也可尝试使用apply查看效果。由于此示例过于简单且无需传递参数,call和apply的功能效果完全一致。

解释上述代码:sayName首先是Function类型的实例,因此具备call方法和apply方法。由于call和apply是Function类型的方法,需通过person.sayName.call(window)这种方式调用,而非person.sayName().call(window)等形式。
call和apply方法的参数为一个作用域(对象),表示将前面的函数在传入的作用域下运行。传入window对象后,sayName方法中的this.name即指向window.name,从而扩充了作用域。
为何传递window和this效果相同?因为当前执行该函数的位置是window,前文提到this指针指向当前作用域,故this即指向window,等同于window。
JS动态构建复杂嵌套JSON对象的方法
2026-11-05 15:41:52
JS负数取余为什么是负数?详解余数与模运算区别
2026-11-05 13:34:41
JS 解压出现乱码或报错怎么解决?
2026-11-05 12:32:38
JS 中 startsWith、indexOf 与正则判断字符串前缀的对比
2026-11-04 22:19:43
JS解析Excel后怎么将数据转换为JSON格式
2026-11-04 20:58:28
