💡 前言
在 JavaScript 中,一切皆"值",而每个值都有其所属的类型。理解 JS 的数据类型是掌握这门语言的基石。根据 ECMA262 规范,JavaScript 共有 8 种数据类型,可以分为两大类:原始数据类型(Primitive Type)和复杂数据类型(Object / Reference Type)。
在 ES6 之前,JS 只有 6 种类型。ES6 新增了 Symbol 和 BigInt,使得类型体系更加完善。
🗺️一、数据类型全景图
JavaScript 数据类型(8 种)
├── 原始数据类型(Primitive Type)— 6 种
│ ├── Number 数值类型
│ ├── BigInt 大整数类型(ES6 新增)
│ ├── String 字符串类型
│ ├── Boolean 布尔值类型
│ ├── Null 空值类型
│ ├── Undefined 未定义类型
│ └── Symbol 唯一标识符类型(ES6 新增)
│
└── 复杂数据类型
└── Object 对象类型(引用数据类型)
📌注意:从规范层面,Number 和 BigInt 同属 Numeric(数值类型)这个内部类别。但从日常开发角度,我们习惯将它们视为独立的类型。

📦二、原始数据类型
原始数据类型的值是不可变的,存储在栈内存中,赋值时是值的拷贝。
2.1 ⚪Null — 空值类型
null 表示一个有意设置为空的对象引用。它的语义是:“这里应该有一个值,但目前没有。”
常见使用场景:
- 初始化一个变量,表示稍后会赋值为对象
- 清空一个变量的值,帮助垃圾回收释放内存
- 表示某个属性暂时不存在
// 表示空,没有
let a = null;
console.log(a); // null — 表示值为空
// 拷贝式赋值:原始类型赋值是"复印机"式的传数据
let b = a; // b 拷贝了 a 的值
b = 2; // 修改 b 不影响 a
console.log(a); // null
console.log(b); // 2
// 利用 null 帮助垃圾回收
let largeobj = {
data: new Array(100000000).fill("hgh")
};
// 当不再需要这个大对象时,置为 null 让垃圾回收器回收内存
largeobj = null;
🔍Null 与 Undefined 的对比
let obj = {
name: 'Alice',
address: null // 有意设置为空 — "我知道 address 存在,但目前没有值"
};
console.log(obj.address); // null
console.log(obj.age); // undefined — "age 这个属性根本不存在"
关键区别:null 是主动设置的空,undefined 是系统告知的"不存在"。
❓2.2 Undefined — 未定义类型
undefined 表示一个未初始化或不存在的变量值。它通常由 JavaScript 引擎自动返回,而非开发者主动赋值。
触发 undefined 的四种典型场景:
// 场景一:声明变量但未赋值
let a; // 声明变量,未赋值,未初始化
console.log(a); // undefined
// 场景二:访问对象不存在的属性
let obj = {};
console.log(obj.property); // undefined
// 场景三:函数没有返回值
function noReturn() {
// 函数体内没有 return 语句
}
console.log(noReturn()); // undefined
// 场景四:访问不存在的数组索引
let arr = [1, 2, 3];
console.log(arr[5]); // undefined — 索引 5 不存在
✅最佳实践:开发中尽量使用
null来主动表示"空值",让undefined保持其系统语义——“这个东西还没被定义”。
🔢2.3 Number — 数值类型
JavaScript 中的数字统一使用64 位双精度浮点数(IEEE 754)来表示,这意味着它不擅长精确计算,尤其是小数运算。
// JS 用二进制存储数值,某些十进制小数无法精确表示
// 就像 1/3 在十进制中是 0.3333333333333333... 无限循环
let a = 0.1;
let b = 0.2;
console.log(a + b); // 0.30000000000000004 — 不是精确的 0.3!
这是浮点数运算的通病,并非 JS 独有。处理涉及金额等需要精确计算的场景时,通常会将小数转为整数计算,或使用专门的库。
🧮2.4 BigInt — 大整数类型(ES6 新增)
当数字超出 Number 的安全整数范围(Number.MAX_SAFE_INTEGER,约为 9 千万亿)时,精度会丢失。BigInt 正是为解决这一问题而生——它可以表示任意大的整数。
使用方式:在整数后面加 n 后缀。
// 超大整数,Number 无法精确表示
let num1 = 999999999999999999999999999999999999999999999999999999999999999n;
let num2 = 123456789098765433467324577654789008733233456899003466788924243n;
console.log(num1 + num2, typeof num1); // 结果精确,类型为 bigint
console.log(num1 + 1n); // BigInt 只能与 BigInt 运算,不能混用 Number
⚠️ 注意:BigInt 不能与普通 Number 混合运算,必须显式转换。
2.5 🔑Symbol — 唯一标识符类型(ES6 新增)
Symbol 通过函数调用 Symbol() 创建,每一次调用都会产生一个绝对唯一的值,即使传入相同的描述标签。
// Symbol — 唯一的标识符,用函数创建,属于简单数据类型
// 轻松表达"独一无二"
console.log(Symbol('zhang'));
// Symbol(zhang)
// 即使描述相同,两个 Symbol 也不相等
console.log(Symbol('zhang') === Symbol('zhang'));
// false — 每一个 Symbol 都是唯一的
console.log(typeof Symbol('zhang'));
// "symbol"
// Symbol 可以不传标签
console.log(Symbol()); // Symbol() — 绝对唯一
// 典型用法:给对象添加不会冲突的键名
let obj = {
[Symbol()]: 'value',
prop: '2'
};
// Symbol 作为属性键不会被常规遍历访问到,适合做"隐藏"属性
Symbol 的核心价值在于保证唯一性,常用于:
- 定义对象的私有属性(不会与任何其他键冲突)
- 作为常量枚举值
- 拦截/自定义内置行为(如
Symbol.iterator)
2.6 📝String 和 Boolean
String 表示文本,Boolean 只有 true 和 false 两个值。这两个类型较为直观,此处不再赘述。
🧩三、复杂数据类型:Object
Object 是 JavaScript 中唯一的复杂数据类型(引用类型)。与原始类型不同:
- Object 的实际数据存储在**堆内存(Heap)**中
- 变量本身存储在栈内存(Stack)中,保存的是指向堆内存的引用地址
这导致了赋值行为的关键差异——引用式赋值:
// 引用式赋值:传递的是地址,而非数据的拷贝
let Obj1 = { name: 'Alice' };
let Obj2 = Obj1; // Obj2 拿到了 Obj1 的引用地址,指向同一个堆内存对象
Obj2.company = '快手'; // 通过 Obj2 修改对象
console.log(Obj1, Obj2);
// Obj1 和 Obj2 都变成了 { name: 'Alice', company: '快手' }
// 因为它们指向的是同一个对象!
💡这就是为什么 “修改 Obj2 会影响 Obj1”——它们共享同一块堆内存。理解这一点对于避免意外修改和掌握 React/Vue 的响应式原理都至关重要。
🖥️四、从内存分配视角理解数据类型
要深入理解原始类型和引用类型的差异,我们需要回到计算机体系结构的基石——冯·诺依曼体系:
现代计算设备包含五大部件:
运算器 + 控制器 + 存储器 + 输入设备 + 输出设备
代码的执行过程:
- 代码存放于外存(硬盘)
- 编译/解释时,代码从硬盘调入内存
- 执行上下文被创建,包含变量环境和词法环境
- 执行上下文被推入调用栈(Call Stack)
📚栈内存(Stack)
- 速度快,空间小
- 存储原始数据类型的实际值
- 存储引用数据类型的地址(指针)
- 编译阶段就能确定每个变量占用多少空间
- 函数执行完出栈时,通过指针偏移量即可快速切换到下一个执行上下文——快速、稳定、可预测
🗄️堆内存(Heap)
- 空间大,速度相对慢
- 存储引用数据类型的实际数据(对象本身)
- 内存分配在运行时动态进行
- 由垃圾回收器(GC)负责回收不再被引用的对象
📊图解对比
栈内存(Stack) 堆内存(Heap)
┌──────────────────┐ ┌──────────────────┐
│ a: null │ │ │
│ b: 2 │ │ { name: 'Alice' │
│ c: undefined │ │ ,company:'快手' │
│ │ │ } │
│ Obj1: 0x001 ─────┼──────────────→ │
│ Obj2: 0x001 ─────┼──────────────┘ │
└──────────────────┘ └──────────────────┘
原始类型存"值",引用类型存"地址"
Obj1 和 Obj2 指向堆内存中同一个对象
✨五、总结
| 类型 | 分类 | 存储位置 | 赋值方式 | 说明 |
|---|---|---|---|---|
| Number | 原始类型 | 栈(存值) | 值拷贝 | 浮点数,精度有限 |
| BigInt | 原始类型 | 栈(存值) | 值拷贝 | ES6 新增,任意大整数 |
| String | 原始类型 | 栈(存值) | 值拷贝 | 不可变字符串 |
| Boolean | 原始类型 | 栈(存值) | 值拷贝 | true / false |
| Null | 原始类型 | 栈(存值) | 值拷贝 | 主动设置的空 |
| Undefined | 原始类型 | 栈(存值) | 值拷贝 | 系统告知的"不存在" |
| Symbol | 原始类型 | 栈(存值) | 值拷贝 | ES6 新增,唯一标识符 |
| Object | 引用类型 | 栈存地址+堆存数据 | 引用传递 | 包含 Array、Function 等 |
💡理解这些基础,是后续深入学习原型链、深浅拷贝、响应式原理、内存管理等一系列高级话题的起点。把根基打牢,上层建筑才能稳固。
🏷️ 掘金标签
JavaScript 数据类型 前端基础 ES6 面试

381

被折叠的 条评论
为什么被折叠?



