JavaScript 有哪些数据类型?它们在内存里是怎么存的?

有时候对 JavaScript 的代码很是疑惑,我写过这样一段代码:

javascript 复制代码
let user1 = { name: '小明' };
let user2 = user1;
user2.name = '小红';
console.log(user1.name); // 结果居然是 "小红"?

我当时愣住了:我只是改了 user2,为什么 user1 也跟着变了?

而换成数字时,却完全不是这样:

javascript 复制代码
let a = 10;
let b = a;
b = 20;
console.log(a); // 还是 10,没变

明明都是赋值,行为怎么差这么多?

后来我才明白,问题的关键并不是代码的写法问题,而是在 JavaScript 的数据类型和它们在内存中的存储方式

搞懂这一点,很多看似奇怪的行为就都顺理成章了。


一、JavaScript 有几种数据类型?

根据 ECMAScript 标准,JavaScript 的数据类型分为两大类:

1. 原始类型------共 7 种

这些类型的特点是:值不可变、直接表示数据本身

类型 示例 说明
number 42, 3.14, NaN 所有数字,包括整数、浮点数、特殊值
string 'hello', "JS" 字符串,不可变
boolean true, false 布尔值
undefined let a; console.log(a); 变量已声明但未赋值
null let b = null; 表示空或无值(注意:它是原始类型!)
symbol Symbol('id') ES6 引入,唯一且不可变的标识符
bigint 123n, 9007199254740991n ES2020 引入,用于表示任意大的整数

typeof null 返回 "object" 是 JavaScript 的一个历史性 bug,至今未修复。但从语言设计上,null 属于原始类型。


2. 引用类型------统称对象

除了上述 7 种,其他所有值都是对象,属于引用类型,包括:

  • 普通对象:{ name: 'Tom' }
  • 数组:[1, 2, 3]
  • 函数:function() {}
  • 日期:new Date()
  • 正则:/abc/
  • 甚至 MapSetPromise

引用类型的核心特点是:可变、通过引用来访问实际数据


二、它们在内存中怎么存储?

这是理解一切行为差异的根源!

计算机内存大致分为两个区域:

  • 栈(Stack):速度快,空间小,用于存储简单、固定大小的数据。
  • 堆(Heap):空间大,速度稍慢,用于存储复杂、动态大小的数据。

原始类型:直接存在栈里

javascript 复制代码
let age = 25;
let isStudent = true;

这两个变量就像两个小抽屉,值本身直接放在栈中

yaml 复制代码
栈(Stack)
┌─────────────────┐
│ age: 25         │
│ isStudent: true │
└─────────────────┘

当你复制一个原始值:

javascript 复制代码
let myAge = age; // 把 25 复制一份
myAge = 30;
console.log(age); // 仍然是 25

因为 myAge 拿到的是 全新的副本 ,和 age 完全无关。

所以原始类型是按值进行传递(Pass by Value)。


引用类型:栈存地址,堆存真实对象

javascript 复制代码
let user = { name: 'Alice', age: 25 };

这时发生了两件事:

  1. 对象 { name: 'Alice', age: 25 } 被创建,存放在堆中。
  2. 变量 user 并不直接包含这个对象,而是保存一个指向堆中对象的引用地址,这个地址放在栈里。

图示如下:

css 复制代码
栈(Stack)              堆(Heap)
┌──────────────┐      ┌──────────────────────────┐
│ user: [地址A] ├─────→│ { name: 'Alice', age: 25 } │
└──────────────┘      └──────────────────────────┘

当你赋值给另一个变量:

javascript 复制代码
let admin = user; // 复制的是地址,不是对象!
admin.name = 'Bob';
console.log(user.name); // 输出 "Bob"!

因为 adminuser 指向同一个堆中的对象

yaml 复制代码
栈(Stack)              堆(Heap)
┌──────────────┐      
│ user: [地址A] ├──┐   
├──────────────┤  │   ┌──────────────────────────┐
│ admin: [地址A] ├──┼──→│ { name: 'Bob', age: 25 }  │
└──────────────┘  │   └──────────────────────────┘
                  │
                  └───┘

所以引用类型是按共享的方式进行传递(Pass by Sharing)

注意:不是按引用传递,JS 中没有真正的按引用传递


三、深入对比

特性 原始类型 引用类型(对象)
存储位置 栈(Stack) 栈(存引用地址) + 堆(存实际对象)
赋值行为 复制值本身 复制引用地址(多个变量共享同一对象)
是否可变 不可变(操作生成新值) 可变(可直接修改属性)
比较方式 比较值是否相等 比较引用地址是否相同
内存占用 固定、较小 动态、可能很大
典型场景 数字、字符串、布尔值 对象、数组、函数

举个比较的例子:

javascript 复制代码
// 原始类型比较
5 === 5; // true
'hi' === 'hi'; // true

// 引用类型比较
{} === {}; // false!两个不同对象
let a = {}; let b = a;
a === b; // true!同一个对象

四、常见问题

为什么字符串很长,也算原始类型?

虽然字符串可能很长,但 JS 引擎会做内部优化(比如使用指针),但从语言语义 上,字符串是不可变的原始值。任何"修改"都会生成新字符串。

javascript 复制代码
let s = 'hello';
s = s + ' world'; // 创建了新字符串,原 'hello' 未被修改

如何真正复制一个对象,而不是共享?

浅拷贝:只复制第一层属性

javascript 复制代码
let newObj = { ...oldObj };
// 或
let newObj = Object.assign({}, oldObj);

深拷贝:递归复制所有层级(需注意循环引用等问题)

javascript 复制代码
let deepCopy = JSON.parse(JSON.stringify(obj)); // 有局限(不能拷贝函数、undefined 等)
// 推荐使用 Lodash 的 _.cloneDeep()

五、总结

  1. 原始类型:值直接存在栈里,赋值是复制值,互不影响。
  2. 引用类型:栈里存地址,堆里存对象,赋值是复制地址,共享同一对象。
  3. 理解存储机制,是掌握 JS 赋值、函数传参、状态管理、性能优化的基础!

下次当你看到:

javascript 复制代码
let a = obj;
a.x = 10;
console.log(obj.x); // 为什么变了?

你就知道:因为 a 和 obj 指向的是同一个对象!

本文首发于公众号:程序员刘大华,专注分享前后端开发的实战笔记。关注我,少走弯路,一起进步!

📌往期精彩

《代码里全是 new 对象,真的很 Low 吗?我认真想了一晚》

《Java 开发必看:什么时候用 for,什么时候用 Stream?》

《这 5 个冷门 HTML 标签,让我直接删了100 行 JS 代码》

《Vue 组件通信的 8 种最佳实践,你知道几种?》

相关推荐
小北方城市网2 小时前
第 10 课:Node.js 后端企业级进阶 —— 任务管理系统后端优化与功能增强(续)
大数据·前端·vue.js·ai·性能优化·node.js
我有一棵树2 小时前
淘宝 npm 镜像与 CDN 加速链路解析:不只是 Registry,更是分层静态加速架构
前端·架构·npm
zhousenshan2 小时前
vue3基础知识100问
前端·vue.js
异界蜉蝣2 小时前
Proxy vs Object.defineProperty:Vue3响应式原理的深度革命
开发语言·前端·javascript
前端早间课2 小时前
Vue3路由实战:优雅封装+灵活拦截,解锁路由配置新姿势
前端·javascript·vue.js
bjzhang752 小时前
使用 HTML + JavaScript 实现级联选择器
前端·javascript·html
无知就要求知2 小时前
golang实现ftp功能简单又实用
java·前端·golang
哥本哈士奇2 小时前
使用Gradio构建AI前端 - RAG召回测试
前端·人工智能
codingFunTime2 小时前
vue3 snapdom 导出图片和pdf
前端·javascript·pdf