ECMAScript原始值与引用值

解释 JavaScript 原始值与引用值在赋值、比较、可变性和内存直觉上的核心差异。

#type / concept #status / growing #resource / javascript #resource / ecmascript

[!info] related notes

ECMAScript原始值与引用值

这篇笔记只回答一个问题: JavaScript 里的值在创建后,为什么会表现出“有的像复制一份,有的像共用同一份对象”。

一句话定义

原始值直接以值本身参与赋值和比较,而对象这类引用值通常通过对象身份参与赋值、共享和比较。

原始值包括什么

  • string
  • number
  • boolean
  • null
  • undefined
  • symbol
  • bigint

这些值的共同点不是“都在栈里”,而是它们在语言语义上都表现为按值使用、不可原地修改。

引用值通常指什么

  • Object
  • Array
  • Function
  • Date
  • RegExp
  • Map / Set

它们的共同点也不是“长得像对象”这么简单,而是变量里通常保存的是“访问该对象的身份”,多个变量可以指向同一个对象。

先建立最重要的直觉

原始值是不可变的

let str = "hello";
str[0] = "H";

console.log(str); // "hello"

str = "Hello"; // 不是改了原字符串,而是让变量重新绑定到一个新值

原始值可以被重新赋值,但值本身不会被原地改写。

引用值通常是可变的

const user = { name: "Alice" };
user.age = 25;

console.log(user); // { name: "Alice", age: 25 }

这里变化的是对象内容,不是变量绑定本身。const 约束的是“不能重新指向别的值”,不等于“对象内部绝对不可变”。

赋值时为什么表现不同

原始值赋值更像复制值

let a = 1;
let b = a;

b = 2;

console.log(a); // 1
console.log(b); // 2

b = a 之后,ab 各自持有自己的值。

引用值赋值更像复制引用

const a = { x: 1 };
const b = a;

b.x = 2;

console.log(a.x); // 2
console.log(b.x); // 2

b = a 复制的不是对象内容,而是“指向同一个对象”的引用。

比较时为什么结果常让人误判

原始值比较的是值

1 === 1; // true
"hi" === "hi"; // true

引用值比较的是身份

const a = { x: 1 };
const b = a;
const c = { x: 1 };

console.log(a === b); // true
console.log(a === c); // false

ac 内容一样,但不是同一个对象,所以结果仍然是 false

内存直觉应该怎么理解

为了建立心智模型,可以先这样理解:

  • 原始值通常表现为“变量直接拿到值”
  • 引用值通常表现为“变量拿到对象地址或身份,再去访问对象内容”

这个模型足够解释大多数赋值、比较和共享行为。真正的引擎实现会更复杂,但不影响日常判断。

这和垃圾回收有什么关系

引用值的关键不是“放在堆里”这句话本身,而是对象什么时候还可达。

  • 只要还有变量、闭包、定时器、全局对象等路径能访问它,它就可能继续存活
  • 当对象不再可达时,GC 才有机会回收它

延伸阅读: ecmascript-memory-management

为什么 React 会反复强调不可变更新

React 的状态更新、React.memo 和很多性能优化,都强依赖“新旧值是不是同一个身份”。

// 错误:原地修改,引用没变
user.age = 19
setUser(user)

// 推荐:创建新对象,让身份变化
setUser({
  ...user,
  age: 19,
})

如果你直接改原对象,再把同一个引用传回去,React 很多比较逻辑会认为“还是原来那个值”。

常见误区

1. “引用类型” 不等于 “一定深拷贝才安全”

只有在你确实要切断共享修改时,才需要创建新对象。很多场景只需要浅拷贝一层就够了。

2. const 让对象不可变

错。const 只保证变量绑定不重新赋值,不保证对象内部不可改。

3. “原始值在栈、对象在堆” 就是完整真相

这只是入门心智模型。它能帮助理解行为,但不要把它当成 ECMAScript 规范层面的精确定义。

最短记忆方式

原始值主要看“值有没有变”,对象主要看“是不是同一个对象”。

创建于 2026/3/24 更新于 2026/5/27