JavaScript引用类型与call/apply作用域详解

作者:纸飞机时间:2026-10-30 06:18:53浏览:549

本文解析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(),可以看到:

JavaScript引用类型概述

此时,person作为一个容器,其中创建了sayName方法(函数)。执行时,必须在person作用域下运行。若在最下方直接执行,即在window作用域下执行,会报错not defined,因为window下未定义sayName方法。其中的this指针较为特殊,它指向当前作用域,this.name即调用当前作用域下的name值。

接下来为window对象添加一个name属性:


window.name = "yujiangshui";

或直接


name = "yujiangshui";

由于window是最大的容器,因此可省略window,所有定义的属性或变量均挂靠到window上,验证如下:

JavaScript引用类型概述

若希望在window这个大容器下运行person小容器内的sayName方法,需使用call或apply来扩充sayName方法的作用域。执行以下语句:


person.sayName.call(window);

或者


person.sayName.call(this);

输出结果一致,也可尝试使用apply查看效果。由于此示例过于简单且无需传递参数,call和apply的功能效果完全一致。

JavaScript引用类型概述

解释上述代码: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。

作者最新文章