深入理解JavaScript的值传递与引用传递机制

作者:纸飞机时间:2026-10-22 18:36:50浏览:538

本文详细解析JavaScript中数据类型的传递机制。基本数据类型(如Number、String)采用值传递,赋值时复制具体数值;而复杂类型(Array、Object等)采用引用传递,变量仅存储内存地址。理解这一区别对于避免函数副作用、实现纯函数及正确处理对象状态至关重要。

JavaScript数据类型与传递机制概述

JavaScript包含5种基本数据类型:布尔值、null、undefined、String和Number。在赋值操作中,这些基本类型通过值传递方式进行处理。此外,还有Array、Function和Object三种类型,它们在底层均被视为对象,并通过引用方式进行传递。

基本数据类型的值传递

基本数据类型

当基本数据类型的值被绑定到某个变量时,可以认为该变量直接包含了该基本数据类型的值。


var x = 10;
var y = 'abc';
var z = null;

使用等号(=)将变量赋值给另一个变量时,实际上是将对应的值进行拷贝,然后将拷贝后的值赋给新变量。这种机制被称为值传递。


var x = 10;
var y = 'abc';
var a = x;
var b = y;
console.log(x, y, a, b) // 10, 'abc', 10, 'abc'

变量a和x均包含数值10,变量b和y均包含字符串'abc',且它们是完全独立的拷贝,互不影响。若修改变量a的值,变量x的内容不会发生任何变化。


var x = 10;
var y = 'abc';
var a = x;
var b = y;
a = 5;
b = 'def';
console.log(x, y, a, b); // 10, 'abc', 5, 'def'

对象类型的引用传递机制

对象类型

当变量绑定到非基本数据类型(如Array、Function、Object)时,该变量仅记录一个内存地址,具体数据存放于该地址指向的内存区域。需要注意的是,指向基本数据类型的变量直接包含数据,而指向非基本数据类型的变量本身并不包含实际数据,仅包含地址引用。

对象在内存中创建。当执行声明语句arr = []时,系统在内存中创建一个数组对象,变量arr记录的是该对象的内存地址。


var arr = []; // (a)
arr.push(1); // (b)

执行步骤(a)后,内存中创建了一个空数组对象,其内存地址标记为#001,变量arr指向该地址。

变量 地址 对象
arr #001 []

执行步骤(b)后,数组对象中增加了一个元素,但数组对象的内存地址保持不变,变量arr的指向也未发生改变。

变量 地址 对象
arr #001 [1]

引用传递机制

对象类型通过引用进行传递,而非值传递。这意味着变量赋值操作仅传递内存地址,而不复制对象内容。


var reference = [1];
var refCopy = reference;

变量 地址 对象
reference #001 [1]
refCopy #001

变量reference和refCopy指向同一个数组对象。如果修改reference所指向的对象内容,refCopy所指向的对象也会受到相应影响,因为它们共享同一内存地址。


reference.push(2);
console.log(reference, refCopy); // [1, 2], [1, 2]

变量 地址 对象
reference #001 [1, 2]
refCopy #001

引用重新赋值与垃圾回收

引用重新赋值

如果对已赋值的变量进行重新赋值,该变量将包含新的数据或指向新的引用地址。


var obj = { first: 'fundebug.com'};
obj = { second: 'fundebug.cn'};

变量obj从指向第一个对象变为指向第二个对象,其内部引用地址随之更新。

变量 地址 对象
obj #001 {first: ‘fundebug.com'}
#002 {second: ‘fundebug.cn'}

如果一个对象不再被任何变量引用,例如第一个对象(地址为#001),JavaScript引擎的垃圾回收机制会自动销毁该对象并释放其占用的内存空间。

引用类型的相等性判断

相等性判断:== 和 ===

对于引用类型的变量,使用==和===运算符时,仅判断引用的内存地址是否相同,而不比较对象内部的属性及值。因此,如果两个变量指向同一个对象,比较结果返回true。


var arrRef = ['Hi!'];
var arrRef2 = arrRef;
console.log(arrRef === arrRef2); // true

如果两个变量指向不同的对象,即使它们包含相同的属性和值,比较结果也会返回false,因为它们的内存地址不同。


var arr1 = ["Hi!"];
var arr2 = ["Hi!"];
console.log(arr1 === arr2); // false

若要判断两个不同的对象在内容上是否完全相同,一种简单的方法是将它们转换为字符串形式,然后比较字符串是否相等。


var arr1str = JSON.stringify(arr1);
var arr2str = JSON.stringify(arr2);
console.log(arr1str === arr2str); // true

另一种方法是递归地判断每一个属性的值,直到触及基本类型为止,然后逐一判断是否相同。

函数参数传递与纯函数

函数参数传递

当将基本类型数据传入函数时,函数会将这些数据拷贝并赋值给函数的参数变量。


var hundred = 100;
var two = 2;
function multiply(x, y) {
 return x * y;
}
var twoHundred = multiply(hundred, two);

变量hundred的值被拷贝给参数变量x,变量two的值被拷贝给参数变量y。

纯函数概念

纯函数是指给定特定输入,始终返回唯一输出,且不对环境产生任何副作用的函数。所有在函数内部定义的变量在函数返回后均被垃圾回收。

然而,如果函数的输入是对象(Array、Function、Object),则传入的是引用。对该变量的操作会直接影响原始对象,这种编程方式会产生副作用,导致代码逻辑复杂且可读性降低。

因此,许多数组方法,如Array.map和Array.filter,是以纯函数的形式实现的。虽然它们的参数是数组变量,但通过深度拷贝并将值赋给新变量,在新数组上进行操作,从而防止原始数组被修改。

以下是一个具体示例:


function changeAgeImpure(person) {
 person.age = 25;
 return person;
}
var alex = {
 name: 'Alex',
 age: 30
};
var changedAlex = changeAgeImpure(alex);
console.log(alex); // { name: 'Alex', age: 25 }
console.log(changedAlex); // { name: 'Alex', age: 25 }

在非纯函数changeAgeImpure中,直接更新对象person的age属性并返回。原始对象alex的内容也被影响,其age属性更新为25。

以下是实现纯函数的示例:


function changeAgePure(person) {
 var newPersonObj = JSON.parse(JSON.stringify(person));
 newPersonObj.age = 25;
 return newPersonObj;
}
var alex = {
 name: 'Alex',
 age: 30
};
var alexChanged = changeAgePure(alex);
console.log(alex); // { name: 'Alex', age: 30 }
console.log(alexChanged); // { name: 'Alex', age: 25 }

通过JSON.stringify将对象转换为字符串,再通过JSON.parse将字符串还原为对象。该操作会生成一个新的对象实例,从而避免修改原始数据。

常见面试题解析

值传递和引用传递是面试中的高频考点,尝试分析以下代码的输出结果:


function changeAgeAndReference(person) {
 person.age = 25;
 person = {
 name: 'John',
 age: 50
 };
 return person;
}
var personObj1 = {
 name: 'Alex',
 age: 30
};
var personObj2 = changeAgeAndReference(personObj1);
console.log(personObj1); // -> ?
console.log(personObj2); // -> ?

总结

以上介绍了JavaScript中值传递与引用传递的核心概念,希望对大家有所帮助。如有任何疑问,欢迎留言,小编会及时回复。感谢大家对本站的支持!

作者最新文章