record 和 tuple 是 javascript 中用于表示深度不可变数据集合的两种新原始类型,语法分别为 #{} 和 #[];但该提案已于2025年4月被tc39正式撤回,不再推进,因此不会出现在es2025及后续标准中。

Record 和 Tuple 是什么,浏览器支持现状如何
Record 和 Tuple 是 JavaScript 提案(Stage 3),不是 HTML 标准的一部分,所以你不能用 HTML 标签或属性“创建”它们。它们是 JS 原生的不可变数据结构,语法形如 #{"name": "Alice", "age": 30}(Record)或 #[1, 2, 3](Tuple)。目前(2024 年中),Chrome 120+、Safari 17.4+ 和 Firefox 125+ 已默认启用,但 Node.js 20.10+ 才开始稳定支持——旧版本直接运行会报 SyntaxError: Invalid token '#'。
常见误操作:把 #{"a": 1} 写在 HTML 的 <script></script> 里却用老版浏览器打开,控制台直接白屏或静默失败;或者误以为加个 type="module" 就能启用,其实和模块类型无关,纯看 JS 引擎是否实现该语法。
怎么在实际项目中安全使用 Record/Tuple
不能依赖全局可用,必须做运行时检测 + 降级。最简判断方式是执行 try { eval('#[]'); } catch { return false; },但生产环境建议用 babel-plugin-record-tuple 编译为普通对象/数组(带 Object.freeze 模拟不可变性)。
- 开发阶段:在 Babel 配置中加入
["@babel/plugin-proposal-record-tuple", { "syntaxOnly": true }],避免编译时出错 - 构建产物:若目标环境不支持,Babel 会把
#[1,2]转成tuple([1,2]),需同时引入 polyfill 库(如@bloomberg/record-tuple-polyfill) - 注意:polyfill 无法完全复现原生性能优势,尤其深层嵌套 Record 查找比原生慢 3–5 倍
Record/Tuple 和 Object.freeze({}) 的关键区别
很多人用 Object.freeze({x: 1}) 试图模拟不可变对象,但这只是浅冻结,嵌套对象仍可变;而 Record 天然深度不可变,且具备值语义(#{a:1} === #{a:1} 返回 true)。
实操陷阱:
-
Object.is(#{a:1}, #{a:1})✅ true;Object.is(Object.freeze({a:1}), Object.freeze({a:1}))❌ false - Record 中不能包含函数、Symbol、undefined 或循环引用,否则语法报错:
SyntaxError: Invalid value in Record - JSON 不支持 Record/Tuple,
JSON.stringify(#{x:1})抛TypeError: Do not know how to serialize a Record,必须手动转成普通对象再序列化
什么时候该用 Record/Tuple,而不是 const 对象
适合场景很明确:需要高性能的值比较、作为 Map/Set 键、或保证配置项绝对不被意外修改。比如 React 中用 Record 作 props 默认值,或 Redux store 里存 UI 状态枚举。
不推荐场景:
- 从 API 接收的 JSON 数据直接包装成 Record(需先校验字段类型,否则
#{id: null}合法但可能引发下游逻辑错误) - 频繁增删属性的配置对象(Record 不支持动态 key,
#{[k]: v}语法非法) - 与后端约定字段名含空格或特殊字符(
#{'user name': 'Alice'}语法错误,必须用字符串键写法#{['user name']: 'Alice'},但此时已失去 Record 的简洁优势)
真正难处理的是跨环境一致性——比如服务端用 TypeScript 编译 JS,但 Node.js 版本不支持 Record 语法,就得统一用 babel 编译链,否则本地跑得通,CI 构建就挂。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











