JavaScript 数据存储详解:类型与内存空间

前言

JavaScript 作为一门动态、弱类型的编程语言,数据存储方式与传统静态语言有很大不同。理解 JS 的数据类型、内存分配机制以及数据存储的底层原理非常重要。本文将围绕"数据类型"、"语言类型特性"以及"内存空间"三个核心点,带你系统梳理 JavaScript 的数据存储原理。


一、JavaScript 的数据类型

1. 原始类型(基本类型)

原始类型是最基本的数据单元,存储在栈内存中,具有不可变性。包括:

  • String :字符串,如 "hello"
  • Number :数字,包括整数和浮点数,如 423.14
  • Boolean :布尔值,truefalse
  • null:表示"无"或"空值"
  • undefined:变量声明但未赋值时的默认值
  • Symbol:ES6 新增,表示独一无二的值,常用于对象属性的唯一标识
  • BigInt:大整数类型,用于处理超出最大安全整数范围的数值

特点:

  • 占用空间小,存储在栈内存
  • 不可变,赋值或操作时会生成新值

2. 引用类型(复杂类型)

引用类型用于存储复杂的数据结构,存储在堆内存中。包括:

  • Object :对象,如 {name: "Hua"}
  • Array :数组,如 [1, 2, 3]
  • Function:函数本质上也是对象
  • Date:日期对象

特点:

  • 占用空间大,存储在堆内存
  • 变量存储的是指向堆内存的引用(地址)
  • 可变性强,修改属性会影响原对象

二、JavaScript 的类型特性

1. 静态语言与动态语言

  • 静态语言:在使用前就需要确定变量的类型,编译时类型已知。例如 Java、C++

  • 动态语言:在使用时不需要确定变量的类型,运行时根据赋值的变量类型来确定。例如 JavaScript、Python

JS 是动态类型语言,变量的类型在运行时根据赋值自动确定。例如:

javascript 复制代码
let a = 10;      // Number
a = "hello";     // String
a = [1, 2, 3];   // Array

2. 强类型与弱类型

  • 强类型语言:不支持隐式类型转换,类型不兼容时会报错

  • 弱类型语言:支持隐式类型转换,类型可以自动转换 JS 支持隐式类型转换。例如:

javascript 复制代码
console.log(1 + "2"); // "12"
console.log("5" - 2); // 3

这种灵活性带来了便利,但也容易引发类型相关的 bug。


三、内存空间与数据存储

1. V8 引擎的内存空间划分

V8 是 Chrome 和 Node.js 使用的 JS 引擎。它在执行 JS 代码时,会划分出三大内存空间:

  • 代码空间:存放代码文本本身
  • 栈空间:用于存储原始类型变量、函数调用上下文等
  • 堆空间:用于存储引用类型数据(对象、数组、函数等)

栈空间(Stack)

  • 结构简单,先进后出
  • 存储原始类型和执行上下文
  • 访问速度快,空间有限

堆空间(Heap)

  • 结构复杂,无序
  • 存储引用类型数据
  • 空间大,访问速度相对慢

2. 原始类型与引用类型的存储方式

  • 原始类型:变量直接存储值本身,分配在栈空间
  • 引用类型:变量存储的是指向堆内存的地址(引用),实际数据存储在堆空间

示例

javascript 复制代码
let a = 10;           // 原始类型,a 存储 10
let b = {num: 1};       // 引用类型,b 存储堆中对象的地址
let c = b;            // c 也指向同一个对象
c.num = 2;
console.log(b.num);     // 2

四、实战面试题

看完以上内容,相信你已经对数据类型与其存储方式已经很了解了,让我们来做一道经典的面试题

javascript 复制代码
function foo(person) {
    person.age = 20
    person = {
        name: '小钟'
    }
    return person
}
let p1 = {
    name: '小华',
    age: 18
}
let p2 = foo(p1)

console.log(p1);
console.log(p2);

运行结果

输出为:

css 复制代码
{ name: '小华', age: 20 }
{ name: '小钟' }

过程详解

  • let p1 = { name: '小华', age: 18 }

    这里,p1 是一个引用类型变量,存储的是对象在堆内存中的地址。

  • let p2 = foo(p1)

    调用 foo 时,person 形参获得了 p1 的地址副本。此时 personp1 都指向同一个对象。

  • person.age = 20

    通过 person 修改对象的 age 属性,由于 personp1 指向同一个对象,所以 p1.age 也变成了 20。

  • person = { name: '小钟' }

    这一步,person 被赋值为一个新对象的地址。此时 person 指向新对象,而 p1 依然指向原来的对象。两者不再关联。

  • return person

    返回的是新对象 { name: '小钟' },赋值给 p2

五、总结

JavaScript 的数据存储机制是理解 JS 语言的基础。原始类型和引用类型的本质区别在于存储位置和可变性。原始类型存储在栈空间,体积小、不可变;引用类型存储在堆空间,体积大、可变。JS 作为动态、弱类型语言,变量类型可以随时变化,类型之间可以自动转换。


相关推荐
编程猪猪侠26 分钟前
Tailwind CSS 自定义工具类与主题配置指南
前端·css
qhd吴飞30 分钟前
mybatis 差异更新法
java·前端·mybatis
YGY Webgis糕手之路1 小时前
OpenLayers 快速入门(九)Extent 介绍
前端·经验分享·笔记·vue·web
患得患失9491 小时前
【前端】【vueDevTools】使用 vueDevTools 插件并修改默认打开编辑器
前端·编辑器
ReturnTrue8681 小时前
Vue路由状态持久化方案,优雅实现记住表单历史搜索记录!
前端·vue.js
UncleKyrie1 小时前
一个浏览器插件帮你查看Figma设计稿代码图片和转码
前端
遂心_1 小时前
深入解析前后端分离中的 /api 设计:从路由到代理的完整指南
前端·javascript·api
你听得到111 小时前
Flutter - 手搓一个日历组件,集成单日选择、日期范围选择、国际化、农历和节气显示
前端·flutter·架构
风清云淡_A1 小时前
【REACT18.x】CRA+TS+ANTD5.X封装自定义的hooks复用业务功能
前端·react.js
@大迁世界1 小时前
第7章 React性能优化核心
前端·javascript·react.js·性能优化·前端框架