Web前端之JavaScrip中的Array、Object、Map和Set详解


一、四种结构的"真实身份"

结构 你以为 引擎里其实是 核心用途
Array 列表 连续内存 + 索引 顺序访问
Object 键值对 字符串键哈希表(有退化路径) 配置/字典
Map 键值对 真正的通用哈希表 高频查找
Set 集合 只有 key 的哈希表 极快存在判断

二、为什么 Object 在大量 key 下会变慢?

很多人不知道:Object 不是一直都是哈希表。
在 V8 里,Object 有两种形态:
1、少量固定 key 时 ------ Hidden Class(快)
像这样:const o = { a: 1, b: 2 };
引擎会创建隐藏类(Hidden Class),属性访问变成:类似 C 结构体的偏移量访问(极快)
2、key 变多 / 动态增删时 ------ 退化为字典模式(慢)
for (let i = 0; i < 100000; i++) { o['k' + i] = i; }
此时:Object 会退化为 Dictionary Mode(哈希 + 链表),性能明显下降,内存也暴涨。


三、为什么 Map 永远稳定快?

const m = new Map();
Map 从设计开始就是:纯哈希表结构,没有 Hidden Class、没有退化路径
特性:
1、任意类型 key
2、不会退化
3、专为高频查找设计
4、内存布局为桶 + 链/开放寻址
所以当 key 很多时:Object <<<< Map差距非常明显。


四、为什么 Set 遍历反而比 Array 慢?

很多人踩这个坑:for (const x of set) {}比for (const x of arr) {}慢很多。
原因:

Array Set
连续内存 哈希桶跳跃
CPU cache 友好 cache miss 严重
指针少 指针多

Set 每次遍历是:桶 → 指针 → 元素 → 下一个桶 → 指针...
这是CPU 最讨厌的访问模式。
所以:Set 快在查找,不快在遍历。


五、真正的内存结构示意

Array(极度友好)
[1][2][3][4][5]
一整块连续内存。


Set / Map(典型哈希桶)

javascript 复制代码
bucket[0] → node → node
bucket[1]
bucket[2] → node
bucket[3] → node → node → node

大量指针跳转。


六、为什么很多人代码慢却不知道原因?

典型误用

javascript 复制代码
const set = new Set(bigList);

for (const x of set) { ... }  // ×

等于用"查找结构"干"遍历结构"的活。


正确模式

javascript 复制代码
const set = new Set(bigList);

for (const x of otherBigList) {
  if (set.has(x)) { ... }   // √
}

七、终极理解(非常关键)

需求 正确结构
频繁遍历 Array
频繁存在判断 Set
大量 key 查值 Map
少量固定字段 Object

这不是语法选择。
是数据结构选择。


八、一句话升维总结

1、Array 是内存结构
2、Set / Map 是索引结构
3、Object 是语法糖结构(会退化)


九、为什么 for (let i=0; i<arr.length; i++) 仍然是 JS 里最快的循环,没有之一

1、先给结论(很重要)

在 V8 里:经典 for 循环之所以最快,本质是最容易被 JIT 编译成"纯机器码循环"


2、V8 是怎么执行 JS 循环的?

JS 代码不会直接执行,而是:
1、解释执行(Ignition)
2、热点代码 JIT 编译(TurboFan)
3、生成优化机器码
for 循环为什么容易被优化?
看这个:

javascript 复制代码
for (let i = 0; i < arr.length; i++) {
  sum += arr[i];
}

V8 可以"确定"几件事:
1、i 是纯数字递增
没有类型变化
2、arr 是连续结构(大概率)
可当成 连续内存数组
3、length 是稳定值
可以 hoist(提取到循环外)
优化后变成类似:

javascript 复制代码
for (int i = 0; i < len; i++) {
    sum += memory_base[i];
}

这已经接近 C 代码了


3、为什么 forEach 更慢?

javascript 复制代码
arr.forEach(x => sum += x);

问题
1、回调函数调用成本
每次循环:函数调用栈 + 参数绑定 + 作用域处理
2、无法 inline(内联优化困难)
JIT 很难把x => sum += x完全折叠进循环里
3、控制流不可预测
return / break / throw 语义复杂


4、为什么 for...of 也不如 for?

javascript 复制代码
for (const x of arr)

问题
1、需要 iterator 协议
底层是arr[Symbol.iterator]()
2、每次 next() 调用
iterator.next()多一层抽象
3、无法完全消除边界检查


5、关键对比(非常重要)

写法 V8 优化等级 真实执行
for (i++) ⭐⭐⭐⭐⭐ 机器码循环
while ⭐⭐⭐⭐ 接近机器码
forEach ⭐⭐ 函数调用循环
for...of ⭐⭐ iterator循环

6、真正的性能核心:不是语法,是"可预测性"

V8 最喜欢的代码特征
类型稳定 → i = number
结构稳定 → arr = packed array
无抽象层 → 没有 callback / iterator
可线性访问 → 连续内存


7、隐藏关键点:数组在 V8 里的真实形态

数组不是"数组",而是:Elements Kind(元素类型系统)

类型 名字 性能
SMI_ELEMENTS 小整数数组 最快
DOUBLE_ELEMENTS 浮点数组 快
OBJECT_ELEMENTS 混合类型 慢

一旦这样做:arr.push("string");
数组直接退化:SMI → OBJECT_ELEMENTS
性能下降一个档次


8、为什么 for 是"终极循环形态"?

1、没有函数调
2、没有 iterator
3、没有 closure 捕获
4、没有协议层
5、可以完全编译成 CPU 指令循环


9、一句话终极本质

for 循环之所以最快,不是因为语法简单
而是因为它最接近 CPU 的真实执行模型


十、CPU + 编译器级别

ChatGPT详解

相关推荐
子兮曰2 小时前
把 649 种文件格式塞进一个网页里:一个「文件不出浏览器」的预览器
前端·javascript·后端
paopaokaka_luck5 小时前
基于springboot+vue的智慧家政管理系统(AI问答、 WebSocket实时沟通、ECharts运营分析、订单规则与服务追踪)
java·javascript·spring boot·数据分析·echarts·mybatis
sm_926787055 小时前
RFID 标签打印的技术实现要点与二次开发实践
java·大数据·前端·c++·编辑器
IMPYLH6 小时前
HTML 的 <video> 元素
前端·html
溪语流沙6 小时前
【每天一个CSS | Day19】流光文字与一笔写成的手写签名
前端·javascript·css
逐光者9336 小时前
STM32——SPI 屏 · Flash · 高速
java·前端·stm32
To_OC6 小时前
同样是让 AI 写全栈,为什么别人一句话就够了
前端·前端框架·next.js
CDwenhuohuo7 小时前
electron pc项目打包成桌面端
前端·javascript·electron
前端大斗师7 小时前
「大于 1000」把 1000 元那单也算进去了:我在 Vue3 订单页对了 10 句话
前端·人工智能·typescript·大模型·原力计划
Jmyd01237 小时前
用 Web VR 引擎搭建 3D 虚拟展厅:从素材上云到多端实时渲染的完整数据链路
前端·3d·vr·三维数字化