目录
- 前言
- 一、前端数据结构
- 二、主流前端框架所采用的主要数据结构对比
-
- 1、主流前端框架核心数据结构对比总表
- 2、核心模块「拆解级」数据结构对照(重点)
-
- [(1)、虚拟 DOM & 组件结构](#(1)、虚拟 DOM & 组件结构)
- [(2)、响应式 / 状态追踪(最容易被忽略)](#(2)、响应式 / 状态追踪(最容易被忽略))
- [(3)、更新调度 / 优先级(高阶)](#(3)、更新调度 / 优先级(高阶))
- (4)、从「数据结构角度」看框架设计哲学
- [三、JavaScript 数据结构「总览」](#三、JavaScript 数据结构「总览」)
-
- [1、js 数据结构「原生 & 非原生」](#1、js 数据结构「原生 & 非原生」)
- [2、js 数据结构大全「对比」(⭐️⭐️⭐️⭐️⭐️)](#2、js 数据结构大全「对比」(⭐️⭐️⭐️⭐️⭐️))
- [四、JavaScript 数据结构「逐个击破」](#四、JavaScript 数据结构「逐个击破」)
-
- 1、线性数据结构(从最基础开始)
- 2、链表(突破数组局限)(⭐)
- 3、哈希结构(性能核心)
-
- [(1)、Object ------ 老牌键值对(⭐)](#(1)、Object —— 老牌键值对(⭐))
- [(2)、Map ------ 真正的哈希表(⭐)](#(2)、Map —— 真正的哈希表(⭐))
- [(3)、Set ------ 去重神器(⭐)](#(3)、Set —— 去重神器(⭐))
- 4、树结构(前端的骨架)
-
- (1)、树(Tree)(⭐)
- [(2)、二叉树 / 平衡树(了解)](#(2)、二叉树 / 平衡树(了解))
- (3)、完全二叉树(了解)
- 5、堆(⭐)
- 6、图结构(Graph)(⭐)
- 7、组合型结构(工程必会)
-
- [(1)、LRU Cache(最近最少使用缓存)(⭐️)](#(1)、LRU Cache(最近最少使用缓存)(⭐️))
前言
本文主要讲的是数据结构,不讲对应数据结构的算法。
相关数据结构的算法实现请看:前端视角下的算法(algorithm)
一、前端数据结构
前端数据结构 ≠ 仅仅是 JavaScript 语言层面的数据结构,但:前端 90% 的数据结构问题,确实是用 JavaScript 数据结构来承载和实现的。
前端数据结构主要是 JS 数据结构,但绝不只限于 JS 数据结构。
| 层级 | 是否属于前端数据结构 | 具体是什么 | 举例 | 是否基于 JS |
|---|---|---|---|---|
| JS 语言层 | ✅ 是 | Array / Map / Set / Object | 列表、缓存 | ✅ |
| 抽象数据结构 | ✅ 是 | 栈 / 队列 / 树 / 图 / 堆 | 撤销、关系图 | ✅(用 JS 实现) |
| 框架内部结构 | ✅ 是 | VDOM、Fiber、依赖图 | React / Vue | ❌(抽象层) |
| 浏览器内部结构 | ⚠️ 间接相关 | DOM Tree、Render Tree | 页面渲染 | ❌ |
| 协议 / 网络结构 | ❌ 不是 | HTTP 报文结构 | 请求响应 | ❌ |
| 数据模型设计 | ✅ 是 | Schema / DSL / JSON 结构 | 表单、低代码 | ✅ |
二、主流前端框架所采用的主要数据结构对比
前端框架的差异,本质不是 API,而是------"用什么数据结构,解决什么规模的问题。"
- React:用链表 + 优先队列解决大规模 UI 调度
- Vue:用树 + 图解决响应式开发体验
- Solid:直接用图,跳过虚拟 DOM
- Svelte:把数据结构提前到编译期
若不理解数据结构,就只能"会用 API,不会设计系统"。
1、主流前端框架核心数据结构对比总表
| 框架 | 虚拟 DOM 结构 | 更新 / Diff 结构 | 组件组织结构 | 状态存储结构 | 依赖收集 / 响应式 | 调度 / 任务结构 | 路由结构 | 关键说明(本质) |
|---|---|---|---|---|---|---|---|---|
| React | 树(Virtual DOM Tree) | 链表(Fiber) | 树 + 链表 | Map / Object | Map + Set | 优先队列(Lane) | 树 | 用链表打断递归,实现可中断渲染 |
| Vue 2 | 树 | 树 Diff | 树 | Object | 依赖图(Graph) | 队列 | 树 | Object + getter/setter |
| Vue 3 | 树 | 树 Diff | 树 | Map / Object | 图(Graph)+ Map + Set | 队列 | 树 | Proxy + 精准依赖收集 |
| Angular | 树 | 树 Diff | 树 | RxJS(流) | 观察者图 | 任务流 | 树 | 强依赖响应式流 |
| Svelte | ❌ 无 VDOM | 编译期生成 | 静态结构 | 变量 | 编译期依赖图 | 编译期 | 树 | 数据结构在编译期展开 |
| Next.js | React 同 | React 同 | 树 | Map | React 同 | React 同 | 文件系统树 | 路由即文件树 |
| Nuxt | Vue 同 | Vue 同 | 树 | Map | Vue 同 | Vue 同 | 文件系统树 | Vue + SSR 封装 |
| Solid.js | ❌ | ❌ | 依赖图 | Signal Map | 细粒度图 | 调度队列 | 树 | 图优先于树 |
2、核心模块「拆解级」数据结构对照(重点)
(1)、虚拟 DOM & 组件结构
| 框架 | 本质数据结构 |
|---|---|
| React | 树 + 单向链表(Fiber) |
| Vue | 树 |
| Angular | 树 |
| Svelte | 编译期静态结构 |
| Solid | 依赖图(Graph) |
只有 React 把"树"拆成了"链表"
(2)、响应式 / 状态追踪(最容易被忽略)
| 框架 | 核心结构 |
|---|---|
| Vue 2 | 图(Watcher 依赖) |
| Vue 3 | 图 + Map + Set |
| React | Map(Hook 索引) |
| Solid | 细粒度依赖图 |
| Angular | Observable 图 |
Vue / Solid 本质是图系统
(3)、更新调度 / 优先级(高阶)
| 框架 | 使用的数据结构 |
|---|---|
| React | 优先队列(Lane)+ 链表 |
| Vue | 队列 |
| Angular | 事件流 |
| Solid | 调度队列 |
只有 React 有"类操作系统级调度"
(4)、从「数据结构角度」看框架设计哲学
| 框架 | 核心偏好数据结构 | 设计哲学 |
|---|---|---|
| React | 链表 / 队列 / 堆 | 可中断、可调度 |
| Vue | 树 / 图 | 易用、直观 |
| Angular | 流 / 图 | 企业级、强约束 |
| Svelte | 编译期结构 | 运行时极简 |
| Solid | 图 | 极致性能 |
三、JavaScript 数据结构「总览」
1、js 数据结构「原生 & 非原生」
JavaScript 只有 4 种原生数据结构类型:
- Array
- Object
- Map
- Set
其余所有:
- 栈
- 队列
- 链表
- 树
- 堆
- 图
- LRU Cache
全部都是"抽象数据结构(ADT)",用 JS 原生结构实现。
2、js 数据结构大全「对比」(⭐️⭐️⭐️⭐️⭐️)
| 数据结构 | 中文名称 | 是否 JS 原生 | 核心特性 | JS 常见实现 | 时间复杂度(核心) | 空间复杂度 | 前端典型应用场景 | 重点 / 易错点 |
|---|---|---|---|---|---|---|---|---|
| Array | 数组 | ✅ | 连续内存、下标访问 | [] |
访问 O(1),插入尾 O(1),插入头 O(n) | O(n) | 列表、表格、虚拟列表 | shift/unshift 性能差 |
| Stack | 栈 | ❌ | 后进先出(LIFO) | Array.push / pop |
入栈 O(1),出栈 O(1) | O(n) | 撤销重做、路由回退、DFS | 栈只是抽象结构 |
| Queue | 队列 | ❌ | 先进先出(FIFO) | 双指针 / 对象 | 入队 O(1),出队 O(1) | O(n) | BFS、任务调度、消息队列 | 不推荐用 shift() |
| Deque | 双端队列 | ❌ | 两端可进出 | 自实现 | 入队 O(1),出队 O(1) | O(n) | 滑动窗口、调度系统 | JS 无原生支持 |
| Linked List | 链表 | ❌ | 非连续内存 | 节点对象 {value, next} |
查找 O(n),插入 O(1) | O(n) | LRU Cache、频繁插删 | 不支持随机访问 |
| Object | 对象 | ✅ | 哈希键值对 | {} |
查找 O(1) | O(n) | 配置、低频 Map | key 仅 string/symbol |
| Map | 映射 | ✅ | 真正哈希表 | new Map() |
查找 O(1) | O(n) | 状态管理、缓存 | 推荐替代 Object |
| Set | 集合 | ✅ | 自动去重 | new Set() |
查找 O(1) | O(n) | 去重、visited 集合 | 不存 key-value |
| Tree | 树 | ❌ | 层级结构 | 递归对象 | 遍历 O(n) | O(n) | 菜单、权限、DOM、路由 | DFS/BFS 必会 |
| Binary Tree | 二叉树 | ❌ | 每节点 ≤2 子节点 | 节点对象 {value, left, right} |
平衡树查找 O(log n),最坏 O(n) | O(n) | 搜索、排序 | 是否平衡关键 |
| Heap | 堆 | ❌ | 完全二叉树 | 数组模拟 | 插入 O(log n),删除 O(log n) | O(n) | Top K、优先队列 | JS 无原生堆 |
| Graph | 图 | ❌ | 点 + 边 | 邻接表/邻接矩阵 | 遍历 O(V+E) | O(V+E) | 关系图、知识图谱 | 防止死循环 |
| Trie | 字典树 | ❌ | 前缀共享 | 嵌套对象 | 查找 O(k) | O(总字符数) | 搜索提示、关键词 | 空间换时间 |
| Hash Table | 哈希表 | ⚠️ 间接 | key → value | Map / Object | 查找 O(1) | O(n) | 几乎所有系统 | 冲突由 JS 引擎处理 |
| LRU Cache | 最近最少使用 | ❌ | 淘汰策略 | Map + 双向链表 | get/put O(1) | O(n) | 缓存淘汰 | 高频面试题 |
| Priority Queue | 优先队列 | ❌ | 按权重出队 | 堆 | 插入/删除 O(log n) | O(n) | 调度系统、Top K | ≠ 普通队列 |
四、JavaScript 数据结构「逐个击破」
从数据结构的角度看前端(一种另类的视角):前端 = Array + Map + Tree + Graph
- Array → 渲染
- Map → 状态 & 性能
- Tree → 结构
- Graph → 关系
1、线性数据结构(从最基础开始)
(1)数组(Array)------一切的起点(⭐)

数组是用连续内存空间存储元素的一种线性数据结构,通过索引快速访问每个元素(数组 = 线性关系 + 连续内存 + 索引访问)。
特点:
- 连续内存存储
- 顺序线性
- 固定 / 动态长度(可扩展性)
为什么快?
- arr[1000] → 直接定位内存
- 访问 O(1)
JS 中的核心操作:
typescript
const arr = [1, 2, 3]
arr.push(4) // 尾部插入 O(1)
arr.pop() // 尾部删除 O(1)
arr.unshift(0) // 头部插入 O(n)
arr.shift() // 头部删除 O(n)
时间复杂度:
| 操作 | 复杂度 |
|---|---|
| 随机访问 | O(1) |
| 尾部插入/删除 | O(1) |
| 中间/头部插入 | O(n) |
前端真实用途:
- 列表 / 表格
- 虚拟列表
- Diff 前后的节点集合
❌ 致命坑:
- 大量使用 shift / unshift
- 在大数组里频繁 splice
(2)、栈(Stack)------后进先出(LIFO)(⭐)

特点:
- 内存连续
- 大小固定
- 访问快:入栈和出栈的时间复杂度都是 O(1)
JS 是否原生?
- ❌ 不是类型
- ✅ 行为可用 Array 表达
JS 实现:
typescript
const stack = []
stack.push(1)
stack.push(2)
stack.pop() // 2
前端真实用途:
- 撤销 / 重做
- 路由历史
- DFS(Depth-First Search,即:深度优先搜索)
- 调用栈(JS 引擎)
关键认知:
- 递归 = 系统栈
- JS 基本类型存栈,引用类型存堆(栈存指针) 。访问基本类型快,引用类型灵活可变。
(3)、队列(Queue)------先进先出(FIFO)(⭐)

特点:
- 线性结构:队列是一个线性列表,元素之间有前后关系
- 受限访问:只能从队尾入队,队头出队,不允许随机访问中间元素
- 先进先出(FIFO):保证数据处理顺序,适合排队、任务调度、缓冲区等场景
- 动态可扩展:队列大小可根据数据增长而变化(数组 + 链表都可实现)
- 可用于抽象概念:广度优先搜索(BFS)、消息队列、打印任务调度等
错误实现(常见):
typescript
arr.push(x)
arr.shift() // ❌ O(n)
正确实现(双指针):
typescript
class Queue {
constructor() {
this.data = {}
this.head = 0
this.tail = 0
}
enqueue(x) {
this.data[this.tail++] = x
}
dequeue() {
const val = this.data[this.head]
delete this.data[this.head++]
return val
}
}
前端用途:
- BFS(Breadth-First Search,即:广度优先搜索)
- 任务调度
- 消息队列
- Vue / React 更新队列
(4)、双端队列(Deque)(⭐)
虽然双向队列与普通队列都属于线性数据结构,但二者存在本质差异:
| 结构 | 访问规则 | 插入/删除位置 |
|---|---|---|
| 队列(Queue) | FIFO(先进先出) | 只能从 队尾入队 ,从 队头出队 |
| 双向队列(Deque) | 双端可操作 | 队头/队尾都可入队或出队 |
双向队列是队列的拓展,增加了双端操作能力,本质依旧是线性结构,但操作更灵活,可同时支持两端的插入和删除。
特点:
- 双端操作(头尾都可进出)
- 线性顺序
- 支持 FIFO / LIFO
- 高效操作(O(1))
应用:
| 应用场景 | 为什么用双向队列 |
|---|---|
| 滑动窗口问题 | 需要头尾快速入出 |
| LRU 缓存 | 最近访问元素移动到头/尾 |
| 双端任务调度 | 高优先任务可从头插入 |
2、链表(突破数组局限)(⭐)

链表是用节点 + 指针来组织数据的线性关系结构,每个节点指向下一个节点,实现动态存储和顺序访问(链表 = 线性关系 + 指针连接 + 动态内存分配)。
特点:
- 节点动态存储(内存非连续)
- 顺序访问(线性关系)
- 灵活扩展 & 可变长度
链表虽然也是线性数据结构,但它和数组的"线性"有本质上的区别:
- 数组:线性顺序依赖索引(强调顺序位置)
- 链表:线性顺序依赖指针连接(强调节点连接)
| 特性 | 数组 | 链表 |
|---|---|---|
| 内存 | 连续 | 非连续 |
| 访问方式 | 随机访问 O(1) | 顺序访问 O(n) |
| 插入 / 删除 | 中间元素 O(n) | 已知节点 O(1) |
| 线性关系体现 | 通过索引 | 通过指针连接 |
JS 实现链表:
typescript
class Node {
constructor(val) {
this.val = val
this.next = null
}
}
链表复杂度:
- 查找 O(n)
- 插入 O(1)
前端用途:
- LRU Cache(Least Recently Used Cache,即:最近最少使用缓存)
- React Fiber(react 内部的可中断渲染架构)
- 频繁插删结构
❌ 最大缺点:
- 不支持随机访问
3、哈希结构(性能核心)

哈希表是用"确定性映射规则",把"任意 key"直接映射到"存储位置"的一种结构(哈希表 = 映射关系(Key → Value)的 O(1) 近似实现)。
特点:
- 近似 O(1) 的增删改查
- 无序(或弱序)
- 空间换时间
为什么很多人会误以为 Array 是简单的哈希表呢?
因为 arr[0] 的下标也是"key",这看起来像哈希表的 key → value 结构。但其实 Array 的 核心语义是"有序索引表",索引是 连续的非负整数,底层有 "连续内存 / 稀疏数组 / elements kind" 优化。Array 的性能优化是围绕「顺序访问」设计的,不是哈希。所以:
❌ Array ≠ Hash Table
✅ Array = 顺序表(Indexed List)
结论:JS 里只有 Object 和 Map 可以"看作"基于哈希的结构;Array 是顺序表,不能看作哈希表。
(1)、Object ------ 老牌键值对(⭐)
本质:
- 哈希表
- key 自动转字符串
缺点:
- 原型污染
- key 限制
- 顺序不可靠
使用场景:
- 简单配置对象
(2)、Map ------ 真正的哈希表(⭐)
为什么需要 Map?
因为 Object 不适合高频哈希操作。
特点:
- key 可以是任意类型
- 有 size
- 保证插入顺序
typescript
const map = new Map()
map.set(obj, 123)
前端用途:
- 状态管理
- 缓存
- 依赖映射
(3)、Set ------ 去重神器(⭐)
typescript
const set = new Set([1,1,2])
用途:
- 去重
- visited 集合(用来记录"已经访问过的节点",防止重复访问或死循环)
- 防止死循环
4、树结构(前端的骨架)

(1)、树(Tree)(⭐)
树是用"父子层级关系"来组织数据的一种"单源分治结构"(树 = 层级 + 分治 + 约束的图)。
特点:
- 层级清晰
- 递归友好
- 局部隔离
遍历(必须会):
- DFS(深度优先搜索):
typescript
function dfs(node) {
if (!node) return
node.children.forEach(dfs)
}
- BFS(广度优先搜索):
typescript
function bfs(root) {
const queue = [root]
while (queue.length) {
const node = queue.shift()
queue.push(...node.children)
}
}
前端用途:
- 菜单
- 权限
- DOM
- 路由
- 虚拟 DOM
(2)、二叉树 / 平衡树(了解)
二叉树:每个节点最多有两个子节点。
用途:
- 搜索
- 排序
(3)、完全二叉树(了解)
完全二叉树:
- 除了最后一层外,其他各层的节点数都达到最大;
- 最后一层的节点 从左到右连续排列,中间不允许有空缺。
5、堆(⭐)

堆是为了"快速找到极值(最大 / 最小)"而对完全二叉树施加的一种"局部有序约束结构"。
堆主要可分为两种类型:
- 小顶堆 (min heap):任意节点的值
<=其子节点的值。 - 大顶堆 (max heap):任意节点的值
>=其子节点的值。
特点:
- 完全二叉树(结构约束)
- 局部有序(堆序性)
- 大顶堆:父 ≥ 子
- 小顶堆:父 ≤ 子
- 操作稳定在 O(log n)
用途:
- Top K(从一组数据中,找出"最大或最小的 K 个元素")
- 调度系统(顺序、优先级、时间、中断)
- React Lane(思想):React 用来给更新任务标记"优先级"的概念,本质是一个"位掩码优先级队列",用于调度 Fiber 渲染顺序。
关键认知:
JS 基本类型存栈,引用类型存堆(栈存指针) 。访问基本类型快,引用类型灵活可变。
6、图结构(Graph)(⭐)

图(Graph)是用"点 + 边"来描述"多对多的关系网络"。
特点:
- 非线性
- 多对多关系
- 可形成"环"
JS 表达:
typescript
const graph = {
A: ['B', 'C'],
B: ['D']
}
遍历:
- DFS(深度优先搜索)
- BFS(广度优先搜索)
- visited(Set)(记录"已经访问过的节点或元素"的集合,防止重复访问或死循环)
前端用途:
- 人物关系
- 知识图谱
- 依赖系统
- Graphin / G6 / relation-graph
7、组合型结构(工程必会)
(1)、LRU Cache(最近最少使用缓存)(⭐️)
LRU Cache(最近最少使用缓存)是在"容量受限"的前提下,优先保留"最近被访问"的数据的一种淘汰策略结构(LRU = 时间局部性假设 + O(1) 访问的结构组合)。
本质:Map + 双向链表。
特点:
- 所有核心操作都是 O(1)
- 时间局部性驱动
- 空间受限 + 自动淘汰
用途:
- 缓存淘汰
- 浏览器缓存思想

