JavaScript原始值与引用值深度解析:内存、访问与赋值机制

作者:半夏时间:2026-10-25 21:41:26浏览:544

本文深入解析JavaScript中原始值与引用值的区别。涵盖6种基本类型与对象类型的内存存储位置(栈与堆)、访问方式(按值与按引用)、比较逻辑(值相等与引用相等)以及动态属性特性。重点剖析null与undefined的历史渊源及区别,并通过变量赋值示例阐明直接赋值与引用赋值的内存行为差异。

值与引用的相关议题

JavaScript 的数据类型宏观上划分为基本类型与引用数据类型两大范畴

基本数据类型涵盖以下 6 种:

string,symbol,number,boolean,undefined,null

symbol 类型作为 ES6 规范引入的新增基本数据类型。

引用数据类型仅包含 1 种:

object

基本数据类型的值常被称为原始值或简单值,而引用数据类型的值则被称为复杂值或引用值。

二者之间的具体差异如下文逐步解析:

1. 简单值(原始值)

简单值代表了 JavaScript 中数据或信息的最基础或最简形式。因其不可再分,故称为简单值。

具体而言,数字即数字,字符串即字符串,布尔值为 true 或 false,null 与 undefined 即为 null 和 undefined。这些值本身极为简单,无法进一步拆解。

鉴于简单值的数据大小固定,其数据存储于内存的栈区。

为便于理解栈的存取机制,可类比乒乓球盒模型。如下图所示:

值与引用的相关议题

具体代码示例如下:

var str = "Hello World";
var num = 10;
var bol = true;
var myNull = null;
var undef = undefined;
console.log(typeof str); // string
console.log(typeof num); // number
console.log(typeof bol); // boolean
console.log(typeof myNull); // object
console.log(typeof undef); // undefined

此处 null 较为特殊,打印结果为 object,此为历史遗留问题。

该现象源于 JavaScript 首个版本中的一个 bug,且因修复会破坏现有代码而无法修正。

根本原因在于不同对象在底层均表现为二进制,若二进制前三位均为 0,则判定为 object 类型。null 的二进制全为 0,前三位亦为 0,故 typeof 返回 object。

例外情况:打印 null == undefined 时返回 true,此为面试高频考点。

二者均表示“无”的概念。

通常,当尝试访问未声明或未赋值的变量时,将得到 undefined 值。JavaScript 会自动将未初始化的声明变量设为 undefined。

而 null 表示空,无法由 JavaScript 自动赋值,需手动将变量赋值为 null。

为何 JavaScript 要设置两个表示“无”的值?

这同样源于历史原因。

1995 年 JavaScript 诞生之初,仿照 Java 仅设置 null 作为表示“无”的值。依据 C 语言传统,null 被设计为可自动转换为 0。

然而,JavaScript 设计者认为此举尚不充分,主要基于以下两点原因。

  • null 在 Java 中被视为对象,但 JavaScript 数据类型分为原始类型(primitive)和复合类型(complex),设计者认为表示“无”的值不应是对象。
  • JavaScript 初版缺乏错误处理机制,类型不匹配时往往自动转换或静默失败。若 null 自动转为 0,错误将难以察觉。

因此,设计者引入了 undefined。注意:null 先于 undefined 出现,undefined 旨在填补之前的空白。

JavaScript 初版的区分方式如下:

null 是一个表示“无”的对象(空对象指针),转换为数值时为 0;

典型应用场景包括:

  • 作为函数参数,表示该参数非对象。
  • 作为对象原型链的终点。

undefined 是一个表示“无”的原始值,转换为数值时为 NaN。

典型应用场景包括:

  • 变量已声明但未赋值时,其值为 undefined。
  • 调用函数时,未提供应提供的参数,该参数值为 undefined。
  • 对象中未赋值的属性,其值为 undefined。
  • 函数未指定返回值时,默认返回 undefined。

2. 复杂值(引用值)

在 JavaScript 中,对象即为复杂值。因对象可向下拆解为多个简单值或复杂值。

复杂值在内存中的大小未知,因其可包含任意值而非特定已知值,故复杂值数据存储于堆区。

如下图所示:

值与引用的相关议题

具体代码示例如下:

// 简单值
var a1 = 0;
var a2 = "this is str";
var a3 = null

// 复杂值
var c = [1, 2, 3];
var d = {m: 20};

3. 访问方式

按值访问

简单值作为不可细化的值进行存储和使用,引用它们会转移其值。

var str = "Hello";
var str2 = str;
str = null;
console.log(str,str2); // null "Hello"

引用访问

复杂值通过引用进行存储和操作,而非实际值。创建包含复杂对象的变量时,其值为内存中的引用地址。引用复杂对象时,使用其名称(即变量或对象属性)通过内存中的引用地址获取该对象值。

var obj = {};
var obj2 = obj;
obj.name = "zhangsan";
console.log(obj.name); // zhangsan
console.log(obj2.name); // zhangsan

4. 比较方式

简单值采用值比较,复杂值采用引用比较。复杂值仅在引用相同对象(即拥有相同地址)时才相等。即使包含相同对象的两个变量也不相等,因为它们不指向同一对象。

示例 1:

var a = 10;
var b = 10;
var c = new Number(10);
var d = c;
console.log(a === b); // true
console.log(a === c); // false
console.log(a === c); // false
console.log(a == c); // true
d = 10;
console.log(d == c); // true
console.log(d === c); // false

示例 2:

var obj = {name : 'zhangsan'};
var obj2 = {name : 'zhangsan'};
console.log(obj == obj2); // false
console.log(obj === obj2); // false
var obj3 = {name : 'zhangsan'};
var obj4 = obj3;
console.log(obj3 == obj4); // true
console.log(obj3 === obj4); // ture

5. 动态属性

对于复杂值,可添加、修改或删除其属性和方法;但简单值不可:

var str = 'test';
str.abc = true;
console.log(str.abc); // undefined
var obj = {};
obj.abc = true;
console.log(obj.abc); // true

复杂值支持动态对象属性,因我们可以定义对象、创建引用并更新对象,所有指向该对象的变量均会获得更新。

新变量指向现有复杂对象,并未复制该对象。此即复杂值有时被称为引用值的原因。复杂值可根据需求拥有任意多个引用,即使对象改变,它们仍指向同一对象。

var obj = {name : 'zhangsan'};
var obj2 = obj;
var obj3 = obj2;
obj.name = 'abc';
console.log(obj.name, obj2.name, obj3.name);
// abc abc abc

6. 变量赋值

最后讨论变量赋值,可分为直接赋值和引用赋值。直接赋值指将简单值赋给变量,引用赋值指将复杂值的引用赋给变量,该引用指向堆区实际存在的数据。

直接赋值

var a = 3;
var b = a;
b = 5;
console.log(a); // 3

引用赋值

var a = {value : 1};
var b = a;
b.value = 10;
console.log(a.value); // 10

灵魂拷问

  • JS 的基本数据类型有哪些?基本数据类型和引用数据类型的区别

参考答案:

在 JavaScript 中,数据类型宏观上划分为基本类型与引用数据类型两大范畴

基本数据类型涵盖以下 6 种:

string,symbol,number,boolean,undefined,null

symbol 类型作为 ES6 规范引入的新增基本数据类型。

引用数据类型仅包含 1 种:

object

基本数据类型的值常被称为原始值或简单值,而引用数据类型的值则被称为复杂值或引用值。

两者的区别在于:

原始值代表 JavaScript 中数据或信息的最基础或最简形式。简单类型的值被称为原始值,因其不可再分。

具体而言,数字即数字,字符即字符,布尔值为 true 或 false,null 与 undefined 即为 null 和 undefined。这些值本身极为简单,无法进一步拆解。由于原始值的数据大小固定,其数据存储于内存的栈区。

在 JavaScript 中,对象即为引用值。因对象可向下拆解为多个简单值或复杂值。引用值在内存中的大小未知,因其可包含任意值而非特定已知值,故引用值数据存储于堆区。

最后总结两者的区别:

访问方式

  • 原始值:访问到的是值
  • 引用值:访问到的是引用地址

比较方式

  • 原始值:比较的是值
  • 引用值:比较的是地址

动态属性

  • 原始值:无法添加动态属性
  • 引用值:可以添加动态属性

变量赋值

  • 原始值:赋值的是值
  • 引用值:赋值的是地址

总结

至此,关于 JavaScript 中原始值和引用值的文章介绍完毕,更多相关 js 原始值和引用值内容请搜索本站以前的文章或继续浏览下面的相关文章希望大家以后多多支持本站!

作者最新文章