搞懂 JS 八大数据类型,前端基础与面试一网打尽

💡 前言

在 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 只有 truefalse 两个值。这两个类型较为直观,此处不再赘述。


🧩三、复杂数据类型: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 的响应式原理都至关重要。


🖥️四、从内存分配视角理解数据类型

要深入理解原始类型和引用类型的差异,我们需要回到计算机体系结构的基石——冯·诺依曼体系

现代计算设备包含五大部件:
运算器 + 控制器 + 存储器 + 输入设备 + 输出设备

代码的执行过程:

  1. 代码存放于外存(硬盘)
  2. 编译/解释时,代码从硬盘调入内存
  3. 执行上下文被创建,包含变量环境词法环境
  4. 执行上下文被推入调用栈(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 面试

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值