JavaScript 中什么是原始类型和引用类型?它们之间有什么区别?

3 阅读3分钟

什么是原始类型和引用类型?它们之间有什么区别?

核心答案

JavaScript 中的数据类型分为两大类:

原始类型(Primitive Types)stringnumberbooleannullundefinedsymbolbigint

  • 存储在栈内存
  • 值访问,赋值时复制的是值本身
  • 不可变(immutable)

引用类型(Reference Types)ObjectArrayFunctionDateRegExp

  • 存储在堆内存中,栈中保存的是指向堆的引用地址
  • 引用访问,赋值时复制的是引用地址
  • 可变(mutable)

深入解析

1. 内存存储机制

栈内存 (Stack)              堆内存 (Heap)
┌─────────────────┐       ┌─────────────────┐
│ a: 10           │       │                 │
│ b: 'hello'      │       │  { name: 'Tom' }│ ← 0x001
│ obj: 0x001 ─────│───────│                 │
└─────────────────┘       └─────────────────┘
  • 栈内存:空间小、访问速度快、系统自动分配释放
  • 堆内存:空间大、访问速度相对慢、需要手动或 GC 回收

2. 赋值行为差异

原始类型 - 值复制

let a = 10;
let b = a;  // b 得到 a 的副本
b = 20;
console.log(a); // 10 (a 不受影响)

引用类型 - 引用复制

let obj1 = { name: 'Tom' };
let obj2 = obj1;  // obj2 得到同一个引用地址
obj2.name = 'Jerry';
console.log(obj1.name); // 'Jerry' (obj1 也被修改了)

3. 不可变性的含义

原始类型的"不可变"指的是值本身不可变,而不是变量不可重新赋值:

let str = 'hello';
str[0] = 'H';  // 无效操作
console.log(str); // 'hello' (字符串本身不可变)

str = 'Hello'; // 这是重新赋值,创建了新的字符串

4. 常见误区

误区一:typeof null === 'object'

typeof null  // 'object'  这是 JS 的历史 bug

误区二:认为 const 声明的引用类型不可修改

const arr = [1, 2, 3];
arr.push(4);  // 可以!const 只保证引用地址不变
arr = [1, 2, 3, 4]; // 报错!不能重新赋值

代码示例

比较方式的差异

// 原始类型 - 按值比较
let a = 'hello';
let b = 'hello';
console.log(a === b); // true

// 引用类型 - 按引用比较
let obj1 = { x: 1 };
let obj2 = { x: 1 };
console.log(obj1 === obj2); // false (不同的引用地址)

let obj3 = obj1;
console.log(obj1 === obj3); // true (同一个引用地址)

函数传参的影响

// 原始类型传参
function changeValue(x) {
  x = 100;
}
let num = 10;
changeValue(num);
console.log(num); // 10 (不变)

// 引用类型传参
function changeObj(obj) {
  obj.name = 'changed';
}
let person = { name: 'original' };
changeObj(person);
console.log(person.name); // 'changed' (被修改了)

深拷贝 vs 浅拷贝

// 浅拷贝 - 只复制第一层
let original = { a: 1, b: { c: 2 } };
let shallow = { ...original };
shallow.b.c = 999;
console.log(original.b.c); // 999 (嵌套对象被影响)

// 深拷贝 - 完全独立的副本
let deep = JSON.parse(JSON.stringify(original));
// 或使用 structuredClone(original)

面试技巧

面试官可能的追问方向

  1. 如何判断数据类型?

    • typeof 的局限性(null、数组)
    • instanceof 的原理
    • Object.prototype.toString.call() 最准确
  2. 如何实现深拷贝?

    • 递归实现
    • 循环引用的处理
    • structuredClone API
  3. Symbol 的应用场景?

    • 对象属性键的唯一性
    • 内置 Symbol(如 Symbol.iterator
  4. BigInt 和 Number 的区别?

展示深度的方式

  • 提到 V8 引擎中的 SMI(小整数优化)
  • 解释为什么 typeof null === 'object' 是历史遗留问题
  • 对比其他语言(如 Java)的值类型/引用类型

一句话总结

原始类型存值在栈、按值访问、不可变;引用类型存值在堆、按引用访问、可变。赋值和传参时,原始类型复制值,引用类型复制地址。