从基础到实战:一文吃透 JS Tuples 与 Records 的所有核心用法
从基础到实战一文吃透 JS Tuples 与 Records 的所有核心用法引言JavaScript 数据不可变性的新篇章JavaScript 作为一种动态类型语言长期以来在数据不可变性方面存在短板。虽然我们有const和Object.freeze()但它们只能提供浅层不可变性且使用起来不够直观。2025 年随着 ECMAScript 规范的推进Tuples元组和 Records记录作为新的内置对象类型正式进入 JavaScript 舞台。它们不仅解决了深度不可变性的痛点还为函数式编程、状态管理和数据交换提供了更优雅的解决方案。本文将带你从零开始全面掌握这两种数据结构的核心用法并通过实战案例让你立刻上手。## 什么是 Tuples 和 Records简单来说Tuples 是不可变的、值比较的数组而 Records 是不可变的、值比较的对象。-Tuples类似Array但所有操作都返回新实例且通过值比较而非引用比较。-Records类似Object同样具有深度不可变性和值比较特性。它们的核心优势在于- 深度不可变性内部所有属性或元素都是只读的- 值相等性两个结构相同的数据会被视为相等无论它们是否指向同一个内存地址- 结构共享内部实现使用哈希映射性能优异## 基础语法与创建### 创建 TuplesTuples 使用#[]字面量语法创建javascript// 创建一个简单元组const point #[10, 20];// 创建嵌套元组const nested #[[1, 2], [3, 4]];// 创建混合类型元组const mixed #[hello, 42, true, null];### 创建 RecordsRecords 使用#{}字面量语法创建javascript// 创建一个简单记录const person #{ name: Alice, age: 30 };// 创建嵌套记录const address #{ city: Beijing, district: #{ name: Haidian, code: 100080 } };// 创建包含动态键的记录键必须是字符串或 Symbolconst dynamic #{ [key 1]: value1 };## 核心特性详解### 1. 深度不可变性这是 Tuples/Records 最核心的特性。一旦创建任何尝试修改都会抛出错误javascriptconst nums #[1, 2, 3];nums[0] 100; // TypeError: Cannot assign to read-only propertyconst obj #{ a: 1 };obj.a 2; // TypeError: Cannot assign to read-only property### 2. 值相等性这是与传统数组/对象最大的区别javascript// 传统数组比较[1, 2] [1, 2]; // false引用比较// Tuples 比较#[1, 2] #[1, 2]; // true值比较// 嵌套结构比较const a #{ x: #[1, 2] };const b #{ x: #[1, 2] };a b; // true### 3. 作为 Map 键或 Set 元素由于值相等性Tuples/Records 可以作为Map的键或Set的元素javascriptconst map new Map();map.set(#[1, 2], point A);map.set(#[3, 4], point B);// 查找时无需引用map.get(#[1, 2]); // point A// 在 Set 中使用const set new Set();set.add(#{ name: Alice });set.add(#{ name: Alice }); // 重复值不会添加set.size; // 1## 进阶操作修改与转换虽然 Tuples/Records 不可变但我们可以通过特定方法创建修改后的新实例javascript// Tuple 的更新方法const original #[1, 2, 3];const updated original.with(1, 99); // #[1, 99, 3]// 添加元素const extended original.push(4); // #[1, 2, 3, 4]const prepended original.unshift(0); // #[0, 1, 2, 3]// 删除元素const sliced original.slice(0, 2); // #[1, 2]const filtered original.filter(x x 1); // #[2, 3]// Record 的更新方法const person #{ name: Alice, age: 30 };const older person.with(age, 31); // #{ name: Alice, age: 31 }// 添加新属性const withEmail person.merge(#{ email: aliceexample.com });// #{ name: Alice, age: 30, email: aliceexample.com }// 删除属性const noAge person.without(age); // #{ name: Alice }## 实战状态管理示例下面是一个完整的可运行示例展示如何在 React 风格的状态管理中使用 Tuples/Recordsjavascript// 模拟 Redux 风格的 reducerimport { produce } from immer; // 传统方式对比// 使用 Tuples/Records 的纯函数状态管理function createStore(initialState) { let state initialState; return { getState: () state, dispatch: (action) { switch (action.type) { case ADD_TODO: // 创建新状态避免直接修改 state state.with(todos, state.todos.push(#{ id: action.id, text: action.text, completed: false }) ); break; case TOGGLE_TODO: state state.with(todos, state.todos.map(todo todo.id action.id ? todo.with(completed, !todo.completed) : todo ) ); break; case REMOVE_TODO: state state.with(todos, state.todos.filter(todo todo.id ! action.id) ); break; } } };}// 初始状态const initialState #{ todos: #[], filter: ALL };// 使用示例const store createStore(initialState);store.dispatch({ type: ADD_TODO, id: 1, text: 学习 Tuples });store.dispatch({ type: ADD_TODO, id: 2, text: 学习 Records });const state1 store.getState();console.log(State 1:, state1);// #{ todos: #[#{ id: 1, text: 学习 Tuples, completed: false }, #{ id: 2, text: 学习 Records, completed: false }], filter: ALL }store.dispatch({ type: TOGGLE_TODO, id: 1 });const state2 store.getState();console.log(State 2:, state2);// 第一个 todo 的 completed 变为 true其他不变// 验证不可变性console.log(state1.todos state2.todos); // false不同引用console.log(state1.todos[0] state2.todos[0]); // false修改过的元素console.log(state1.todos[1] state2.todos[1]); // true未修改的元素共享引用## 实战数据查询与缓存Tuples/Records 的值比较特性在数据缓存场景中非常有用javascript// 高性能缓存系统class Cache { constructor() { this.cache new Map(); } get(key) { return this.cache.get(key); } set(key, value) { this.cache.set(key, value); } // 使用 Record 作为复合键 setComplexQuery(query, result) { const key #{ table: query.table, filters: query.filters, sortBy: query.sortBy, page: query.page }; this.cache.set(key, result); } getComplexQuery(query) { const key #{ table: query.table, filters: query.filters, sortBy: query.sortBy, page: query.page }; return this.cache.get(key); }}// 使用缓存const cache new Cache();// 第一次查询模拟数据库const query1 { table: users, filters: #{ age: 25 }, sortBy: name, page: 1 };const result1 fetchFromDatabase(query1); // 假设这是耗时的操作cache.setComplexQuery(query1, result1);// 第二次查询命中缓存const query2 { table: users, filters: #{ age: 25 }, sortBy: name, page: 1 };const cachedResult cache.getComplexQuery(query2);console.log(cachedResult result1); // true命中缓存// 不同的查询未命中const query3 { table: users, filters: #{ age: 30 }, sortBy: name, page: 1 };console.log(cache.getComplexQuery(query3)); // undefined未命中## 性能考虑与最佳实践1.避免频繁创建大型结构虽然 Tuples/Records 内部使用结构共享但频繁创建大型深嵌套结构仍可能造成性能开销。2.优先用于需要值比较的场景如 Map 键、Set 元素、状态管理中需要深度比较的场景。3.结合Object.fromEntries和Array.from转换当需要与旧代码交互时javascript// Tuple → Arrayconst tuple #[1, 2, 3];const arr Array.from(tuple); // [1, 2, 3]// Record → Objectconst record #{ a: 1, b: 2 };const obj Object.fromEntries(record.entries()); // { a: 1, b: 2 }4.注意性能陷阱with方法在大型 Record 上性能较好但频繁对大型 Tuple 进行push或unshift可能不如使用map高效。## 总结Tuples 和 Records 为 JavaScript 带来了真正的深度不可变数据结构解决了长期存在的引用比较问题和可变性管理难题。通过本文的学习你应该已经掌握了-基础创建语法#[]和#{}-核心特性深度不可变性、值相等性-修改方法with、push、filter、map、merge、without等-实战应用状态管理、数据缓存、Map/Set 键值使用在实际项目中建议先从状态管理或数据缓存等场景开始尝试使用 Tuples/Records逐步熟悉它们的工作方式。记住它们不是要完全替代传统数组和对象而是在需要不可变性和值比较的场景下提供更优雅、更高效的解决方案。随着 ECMAScript 规范的进一步完善Tuples 和 Records 将成为 JavaScript 开发者工具箱中不可或缺的利器。现在就动手尝试让你的代码更加健壮、可预测

相关新闻

解决WMI核心dll丢失问题的全面指南

解决WMI核心dll丢失问题的全面指南

1. 问题背景与核心影响解析当你在运行某些依赖Windows管理功能的应用程序时,突然弹出"Microsoft.Management.Infrastructure.Native.Unmanaged.dll文件丢失"的错误提示,这种情况通常发生在以下三种典型场景:系统更新不完整导致WMI组…

2026/9/23 13:10:51 阅读更多 →
深入解析TI DM646x CRGEN模块:MPEG-2 TS流时钟恢复的硬件实现与驱动开发

深入解析TI DM646x CRGEN模块:MPEG-2 TS流时钟恢复的硬件实现与驱动开发

1. 项目概述 在嵌入式音视频处理领域,尤其是处理MPEG-2传输流(TS)这类实时流媒体时,有一个问题始终如影随形:时钟不同步。发送端编码器以它自己的27MHz时钟节奏打包数据,而接收端的解码器则依赖本地晶振产生…

2026/9/19 2:14:25 阅读更多 →
终极指南:如何使用BG3ModManager高效管理博德之门3模组

终极指南:如何使用BG3ModManager高效管理博德之门3模组

终极指南:如何使用BG3ModManager高效管理博德之门3模组 【免费下载链接】BG3ModManager A mod manager for Baldurs Gate 3. This is the only official source! 项目地址: https://gitcode.com/gh_mirrors/bg/BG3ModManager 你是否曾经为《博德之门3》的模组…

2026/9/19 2:46:14 阅读更多 →

最新新闻

【Bug已解决】Codex Desktop 拖拽生成图到 macOS Finder 崩溃:TaoToken 配置与 settings.json 骨架修复

【Bug已解决】Codex Desktop 拖拽生成图到 macOS Finder 崩溃:TaoToken 配置与 settings.json 骨架修复

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/25 9:05:20 阅读更多 →
超越Copilot!用TaoToken统一Key接入Cursor,嵌入式开发效率飙升

超越Copilot!用TaoToken统一Key接入Cursor,嵌入式开发效率飙升

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/25 9:05:20 阅读更多 →
Atlas 300V 24G推理加速卡实战:从环境配置到YOLOv5部署

Atlas 300V 24G推理加速卡实战:从环境配置到YOLOv5部署

1. Atlas 300V 24G到底算不算“运算加速卡”——从昇腾产品线看定位1.1 先回答那个高频问题:300V是什么这几周后台一直有人问我:“Atlas 300V 24G是运算加速卡吗?”问到后来还有一句更具体的:“我想用Atlas部署YOLO,该…

2026/9/25 9:05:20 阅读更多 →
ZTools 插件开发实战:详解 ztools API 对象,剪贴板、模拟输入与持久化存储一网打尽

ZTools 插件开发实战:详解 ztools API 对象,剪贴板、模拟输入与持久化存储一网打尽

ZTools 插件开发实战:详解 ztools API 对象,剪贴板、模拟输入与持久化存储一网打尽 【免费下载链接】ZTools An open-source implementation of uTools, a high-performance, scalable application launcher and plugin platform | Supports macOS and W…

2026/9/25 9:05:19 阅读更多 →
雷电模拟器9.0.56刷Magisk与LSPosed实战指南

雷电模拟器9.0.56刷Magisk与LSPosed实战指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/25 9:05:19 阅读更多 →
Spirent TestCenter 从端口占用到批量建流的完整实践指南

Spirent TestCenter 从端口占用到批量建流的完整实践指南

简介:Spirent-TestCenter简易操作手册聚焦思博伦网络测试仪的典型应用场景,面向网络测试工程师、运维人员及刚接触测试仪表的学习者,系统解决设备性能测试中端口占用、流量配置与启停的常见实操问题。内容覆盖端口占用窗口添加仪表IP地址&…

2026/9/25 9:04:19 阅读更多 →

日新闻

AI元人文:从工具使用到思维重构的深度探索

AI元人文:从工具使用到思维重构的深度探索

最近半年我一直在琢磨一件事:AI元人文到底是什么?说白了,就是“用元视角重新审视人与AI的关系”,也在“探索AI如何反向逼着我们发现自己的思考边界”。标题里的“元探索”,在我看就是一层套一层的追问——当你用AI解决…

2026/9/25 0:00:41 阅读更多 →
Python+CNN车牌识别实战:从数据预处理到模型训练与部署

Python+CNN车牌识别实战:从数据预处理到模型训练与部署

简介:基于Python与卷积神经网络的车牌识别项目,面向计算机视觉初学者及智能交通开发者,目标是帮助用户掌握从数据预处理、模型构建到实际部署的完整流程。压缩包共25个文件,包含jpg/png图像样本、py训练脚本、md说明文档、dat数据…

2026/9/25 0:00:41 阅读更多 →
Vim基础操作全攻略:保存退出、模式切换与高频命令实战

Vim基础操作全攻略:保存退出、模式切换与高频命令实战

1. 项目概述1.1 核心需求解析今天聊聊Vim。写这个题目的原因是:几乎每个后端开发者、运维人员、数据工程师某天都会遇到一个场景——深夜加班,服务器登录界面只有黑底白字,编辑器只有vi/vim,你必须在五分钟内完成一次配置修改并保…

2026/9/25 0:00:41 阅读更多 →

周新闻

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

直接铺开项目本身吧。这几个月我一直在折腾一件事:用Flutter给OpenHarmony做一款游戏集合类的App,说白了就是把若干小游戏塞进一个壳里,用统一入口分发。这个方向本身不算新鲜,真正让我花了不少心思的,是首页那堆游戏卡…

2026/9/24 14:34:13 阅读更多 →
Word表格编号全攻略:从列表编号到题注交叉引用

Word表格编号全攻略:从列表编号到题注交叉引用

写Word文档,最让人头疼的往往是那些“看起来不起眼”的小问题。比如表格编号这事:今天在表后面多加了两个空白行,明天给客户交稿前发现整个章节的编号全部错位,光是挨个改序号就能耗掉大半个下午。我前阵子帮人整理一份上百页的技…

2026/9/24 9:10:42 阅读更多 →
从第一个站到第二个站:独立开发者的静态网站选型与落地实践

从第一个站到第二个站:独立开发者的静态网站选型与落地实践

1. 项目概述1.1 核心需求解析做独立开发者这几年,说实话,第一个网站上线的那天晚上我兴奋得没睡着。但等它跑了半年,流量惨淡、功能臃肿、代码自己都懒得看第二遍之后,我才慢慢琢磨明白一个道理:第一个网站是练手&…

2026/9/24 14:33:56 阅读更多 →

月新闻

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能分类:[AI/大模型]细分主题:AI 增强型 CI/CD 流水线自动化与 GitOps 实践:Agent 工作流、工具调用与任务拆解:从原型到生产的验收清单很多团队在尝试用大…

2026/9/24 12:50:34 阅读更多 →
容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场分类:[工程技术]细分主题:Kubernetes 生产环境运维与排障实战:可复制的项目复盘模板与决策记录大部分团队的事故复盘报告,最后都变成了躺在 Confluence 或钉…

2026/9/24 14:33:48 阅读更多 →
容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步分类:[工程技术]细分主题:Docker 容器化技术与镜像安全管理:核心链路的逐步实现与关键代码取舍面对一个积累了五六年历史包袱的单体架构应用(包含 Web 接口、后台…

2026/9/24 12:49:17 阅读更多 →