【前端八股文面试题】JavaScript中的数据类型?存储上的差别?
文章目录
- 一、数据类型分类
- 二、存储机制的区别
- (1) 原始类型的存储方式
- (2) 引用类型的存储方式
- 三、内存模型图解
- 四、关键差异总结
- 五、常见问题
在 JavaScript 中,数据类型分为两类: 原始类型(基本类型) 和 引用类型(对象类型),它们在内存存储方式上有显著差异。
一、数据类型分类
-
原始类型(Primitive Types):
Number
:数值(整数、浮点数)String
:字符串Boolean
:布尔值(true
/false
)Null
:空值Undefined
:未定义Symbol
:唯一标识符(ES6 新增)BigInt
:大整数(ES11 新增)
-
引用类型(Reference Types):
Object
:普通对象{}
Array
:数组[]
Function
:函数Date
、RegExp
、Map
、Set
等内置对象
二、存储机制的区别
(1) 原始类型的存储方式
-
存储位置:变量直接存储值本身(栈内存 Stack)。
-
行为特点:
- 按值访问:操作变量时直接操作实际值。
- 不可变性:修改值会创建新数据,原数据不变。
- 赋值传递:赋值给其他变量时 复制值。
let a = 10; let b = a; // b 复制 a 的值(10) b = 20; // 修改 b 不影响 a console.log(a); // 10(不变)
(2) 引用类型的存储方式
-
存储位置:变量存储对象在堆内存中的地址(指针),实际数据存在堆中(Heap)。
-
行为特点:
- 按引用访问:变量操作的是对象的地址。
- 可变性:修改对象属性会影响所有指向该地址的变量。
- 赋值传递:赋值给其他变量时 复制地址(非实际对象)。
let obj1 = { name: "Alice" }; let obj2 = obj1; // obj2 复制 obj1 的地址(指向同一对象) obj2.name = "Bob"; // 修改 obj2 会影响 obj1 console.log(obj1.name); // "Bob"(被修改)
三、内存模型图解
栈内存 (Stack) 堆内存 (Heap)
┌─────────────┐ ┌─────────────────────┐
│ 原始类型变量 │ │ │
├─────────────┤ │ 引用类型实际数据 │
│ a = 10 │ │ ┌─────────────────┐ │
│ b = 20 │ │ │ { name: "Bob" } │ │
│ ... │ │ └─────────────────┘ │
│ │ │ │
│ 引用类型变量 │ │ │
│ obj1 ────────┼──────────▶ 地址 0x123 │
│ obj2 ────────┘ │ │
└─────────────┘ └─────────────────────┘
四、关键差异总结
特性 | 原始类型 | 引用类型 |
---|---|---|
存储位置 | 栈内存(直接存储值) | 堆内存(变量存储地址,对象存堆中) |
赋值行为 | 复制值(独立副本) | 复制地址(指向同一对象) |
比较方式 | 值相等 a === b (比较值) | 引用相等 obj1 === obj2 (比较地址) |
可变性 | 不可变(修改创建新值) | 可变(修改影响所有引用) |
内存管理 | 自动回收(随栈帧销毁) | GC 垃圾回收机制跟踪 |
五、常见问题
-
为什么
null === object
?
typeof null
返回"object"
(历史遗留 bug),但null
本质是原始类型。 -
如何检测类型?
- 原始类型:用
typeof
(typeof "abc" → "string"
)。 - 引用类型:用
instanceof
或Object.prototype.toString.call()
。
- 原始类型:用
-
深度克隆对象?
由于引用类型的赋值是浅拷贝,需用JSON.parse(JSON.stringify(obj))
或递归实现深拷贝。
通过理解存储机制的差异,可以避免因引用类型误用导致的数据篡改问题,同时优化内存使用。