A user_4a13383d 2026-09-04 09:22 2 次浏览 Javascript 数据类型以及栈内存和堆内存 变量更像一张写着地址的便签。基本类型把值写在便签上;对象类型写的是住址,所以两个便签可能指向同一间房。 ## 目录 - [一、JavaScript 有哪些数据类型](#一javascript-有哪些数据类型) - [二、基本类型和引用类型](#二基本类型和引用类型) - [三、最小案例:为什么修改会互相影响](#三最小案例为什么修改会互相影响) - [四、边界问题:const 不等于不可变](#四边界问题const-不等于不可变) - [五、栈和堆应该怎么理解](#五栈和堆应该怎么理解) - [六、如何减少意外修改](#六如何减少意外修改) - [七、总结](#七总结) ## 一、JavaScript 有哪些数据类型 JavaScript 类型可以先分成两组: ### 基本类型 - undefined - null - boolean - number - bigint - string - symbol 基本类型表示一个具体值,通常不可变。字符串可以生成新字符串,但不能直接修改原字符串中的某个字符。 ### 对象类型 除了基本类型,其他值都属于对象类型,例如: - 普通对象和数组。 - 函数。 - Date、Map、Set 等内置对象。 - 正则表达式和各种宿主对象。 函数也是对象,只是它多了可调用的能力。 ## 二、基本类型和引用类型 基本类型赋值时,可以把它理解为复制值: ``````javascript let first = 10; let second = first; second = 20; console.log(first); // 10 console.log(second); // 20 `````` 对象赋值时,复制的是引用: ``````javascript const first = { count: 1 }; const second = first; second.count = 2; console.log(first.count); // 2 console.log(second.count); // 2 `````` 这不是“对象住在堆里、变量住在栈里”这么简单的一句话就能解释完。具体存储由 JavaScript 引擎决定;对开发者最有用的结论是:对象赋值和参数传递不会自动复制对象。 ## 三、最小案例:为什么修改会互相影响 ### 1. 浅拷贝只分离外层 ``````javascript const state = { user: { name: 'Ada', }, }; const nextState = { ...state }; nextState.user.name = 'Grace'; console.log(state.user.name); // Grace `````` 展开运算符创建了新的外层对象,但 user 仍然是同一个引用。需要复制嵌套数据时,阅读 [深拷贝和浅拷贝.md](./深拷贝和浅拷贝.md)。 ### 2. 函数参数也遵循同样规则 ``````javascript function rename(user) { user.name = 'Grace'; } const user = { name: 'Ada' }; rename(user); console.log(user.name); // Grace `````` 函数接收到的是对象引用的副本,但这个引用仍然指向原对象,因此可以修改对象内容。重新给参数赋值不会改变调用方变量指向: ``````javascript function replace(user) { user = { name: 'Grace' }; } const user = { name: 'Ada' }; replace(user); console.log(user.name); // Ada `````` ## 四、边界问题:const 不等于不可变 const 只禁止变量重新指向另一个值,不会冻结对象内容: ``````javascript const user = { name: 'Ada' }; user.name = 'Grace'; // 可以 // user = {}; // TypeError `````` 数组也一样: ``````javascript const list = []; list.push('JavaScript'); // 可以 // list = ['Python']; // TypeError `````` 需要不可变数据时,可以使用 Object.freeze(注意它默认是浅层冻结),或者通过不修改原对象的方式创建新对象。 ## 五、栈和堆应该怎么理解 “基本类型在栈,对象在堆”是初学阶段常用的示意图,不是 JavaScript 规范规定的物理布局。引擎可能使用寄存器、内联存储、逃逸分析和其他优化。 更稳妥的理解是: - 基本类型按值使用。 - 对象通过引用访问,可能被多个变量共享。 - 垃圾回收器会回收不可达的对象。 - 变量离开作用域不等于对象立刻销毁;只要仍有引用,对象就会继续存活。 ## 六、如何减少意外修改 ### 1. 只复制需要修改的层 ``````javascript const nextState = { ...state, user: { ...state.user, name: 'Grace', }, }; `````` ### 2. 使用结构化克隆处理数据对象 ``````javascript const copiedState = structuredClone(state); `````` 使用前要确认运行环境和数据类型支持范围,函数、DOM 节点等对象不能默认当成普通数据复制。 ### 3. 通过 API 控制修改入口 与其把一个大对象到处传递,不如只暴露查询和更新方法,让修改发生在少数可追踪的位置。闭包、类私有字段和模块都可以帮助封装状态。 ## 七、总结 - 基本类型按值使用,对象类型通过引用访问。 - 给对象赋值、传参或浅拷贝时,都可能共享嵌套引用。 - const 保护的是变量绑定,不是对象内部内容。 - “栈和堆”的说法适合建立直觉,但不要当成规范级结论。 - 能局部复制就不要全量深拷贝;需要完整复制时,先确认类型和环境支持。 - 内存问题的核心判断是:对象是否仍然可达,而不是它曾经在哪个变量里。 点赞0 收藏0
评论
还没有评论,来分享你的想法吧。