)
教程【免费下载链接】jstipsThis is about useful JS tips!项目地址https://gitcode.com/gh_mirrors/js/jstips点击查看免费下载本文基于 jstips 仓库第 32 期英文原文 map-to-the-rescue-adding-order-to-object-properties.md 展开梳理 JavaScript 普通对象属性顺序不可靠的根源给出 ES6Map这一保序方案并为老浏览器提供可行的降级 hack。读完你将掌握如何在需要按写入顺序读取的场景下用Map与for...of可靠地存取键值数据以及如何在无Map的环境下用双数组或单属性对象数组模拟有序映射。一、问题根源对象的属性在规范上就是无序集合原文档引用 ECMAScript第 3 版对 Object 的定义对象是 Object 类型的成员它是一个无序的属性集合每个属性包含一个原始值、对象或函数。存储在对象属性中的函数称为方法。这一定义意味着普通对象只保证属性的存在与取值并不保证属性的遍历顺序。如果你试图在对象字面量里按顺序保存数据那么在读取时顺序可能完全不是你写入的样子。原文档给出了一个直观的例子var myObject { z: 1, : 2, b: 3, 1: 4, 5: 5 }; console.log(myObject) // Object {1: 4, 5: 5, z: 1, : 2, b: 3} for (item in myObject) { // 实际遍历输出为 // 1 // 5 // z // // b }注意字面量中明明先写了z其次、b、1、5但console.log与for...in展示出的顺序却是1、5、z、、b。整数键1、5被排到了最前面字符串键z、、b才按插入顺序跟在后面——而这还只是该运行环境当时浏览器的具体行为。原文档的结论是每个浏览器都有自己的排序规则因为从技术上来说对象属性的顺序是未规定的unspecified。依赖这种不确定的顺序去实现业务逻辑就是隐患的起点。补充说明面向现代引擎随着 ES2015 及后续规范演进引擎在枚举自有属性时实际上遵循了一套被普遍实现的规则——整数索引键按数值升序优先字符串键按插入顺序Symbol 键按插入顺序排最后。例如本例中1、5是整数索引所以被提到最前。但for...in在规范层面依然包含继承属性与实现细节把顺序保证寄托在对象上仍然是不稳妥的做法。二、解法一用 ES6 Map 获得确定的插入顺序原文档给出的首选方案是 ES6 新增的Map结构Map 对象按插入顺序迭代其元素for...of循环每次迭代返回一个[key, value]数组。var myObject new Map(); myObject.set(z, 1); myObject.set(, 2); myObject.set(b, 3); for (var [key, value] of myObject) { console.log(key, value); } // z 1 // 2 // b 3这里展示了两层关键点写入顺序即读取顺序z、、b按set()的调用顺序原样输出没有整数键被提前也没有任何重排。迭代解构for...of直接解构出[key, value]二元组写法比for...in配合obj[key]取值更简洁、更不易出错。Map 的常用 API 一览把原文档的示例扩展到真实开发场景Map的核心操作如下const map new Map(); // 写入 map.set(z, 1); map.set(, 2); // 读取 map.get(z); // 1 map.has(); // true判断键是否存在 map.size; // 2键值对数量 // 迭代三种视角 for (const [key, value] of map) { /* 键值对插入顺序 */ } for (const key of map.keys()) { /* 仅键 */ } for (const value of map.values()) { /* 仅值 */ } // 遍历回调式 map.forEach((value, key) console.log(key, value)); // 删除与清空 map.delete(z); // 删除指定键 map.clear(); // 清空全部与普通对象相比Map的键可以是任意值包括对象、函数而对象的键只能是字符串或 SymbolMap还自带size属性而对象需要Object.keys(obj).length才能拿到属性数量。这些差异让Map在当作有序字典使用时明显更顺手。三、解法二老浏览器环境下的有序映射 hackMap是 ES6 特性在它普及之前跨浏览器环境下没有天然的保序字典。原文档引用 Mozilla 的建议如果你想在跨浏览器环境中模拟一个有序的关联数组那么你只能二选一要么使用两个独立的数组一个存键另一个存值要么构建一个由单属性对象组成的数组等等。方案 A两个平行数组键数组 值数组// 用两个数组分别保存键与值索引对齐 var objectKeys [z, , b, 1, 5]; var objectValues [1, 2, 3, 4, 5]; for (var i 0; i objectKeys.length; i) { console.log(objectKeys[i], objectValues[i]); } // z 1 // 2 // b 3 // 1 4 // 5 5说明原文档示例中var objectKeys [z, , b, 1, 5];里的z、、b缺少引号在实际运行时会被当作未声明的变量而抛错本文已将其修正为字符串字面量[z, , b, 1, 5]以便代码可以直接复制运行。这个方案的代价是维护成本任何增删操作都必须同时操作两个数组并保持下标对齐否则键值就会错位。方案 B单属性对象数组var myData [{z: 1}, {: 2}, {b: 3}, {1: 4}, {5: 5}];数组本身保证元素顺序每个元素又是一个只含一个属性的对象。读取时遍历数组、取出每项的属性即可myData.forEach(function (entry) { var key Object.keys(entry)[0]; console.log(key, entry[key]); }); // z 1 // 2 // b 3 // 1 4 // 5 5这种结构把顺序交给了数组把键值关联交给了单属性对象是 ES5 时代常见的有序映射模拟手段。缺点是查找某个键时需要线性扫描数据量大时效率不高更适合小规模、写多读少的场景。四、仓库视角这份 tip 在 jstips 中的位置与延伸在 jstips 仓库中本篇是编号 32 的 tip在 README.md 的 tips 列表里登记为Map()to the rescue: adding order to Object properties与第 73 期《Hash maps without side effects》(hash-maps-without-side-effects.md) 恰好形成对照前者解决要不要顺序后者解决用对象当映射时的脏原型问题。当需要保证顺序时首选Map本文主题当只把对象当纯哈希表、不需要顺序时第 73 期建议用Object.create(null)创建无原型的干净映射避免constructor、toString等继承属性污染数据。此外这篇 tip 在仓库中还有完整的简体中文、繁体中文与西班牙语翻译版本可对照阅读中文版2016-02-01-map-to-the-rescue-adding-order-to-object-properties.md繁体中文版2016-02-01-map-to-the-rescue-adding-order-to-object-properties.md西班牙语版2016-02-01-map-to-the-rescue-adding-order-to-object-properties.md五、实践建议与小结围绕对象属性顺序不可靠这一主题按使用场景归纳出如下决策路径场景推荐方案理由现代运行环境Node.js / 现代浏览器ES6Map规范保证插入顺序for...of迭代解构键类型不受限自带size需要兼容无Map的老浏览器双数组或单属性对象数组不依赖对象枚举顺序用数组天然的有序性兜底仅作哈希查找、不关心顺序普通对象或Object.create(null)避免继承属性干扰详见 tip 73最后记住原文档的核心告诫普通对象是无序的属性集合把需要顺序的数据写进对象字面量等于把正确性押在引擎的未规定行为上。只要你的数据需要按写入顺序被消费就应该优先考虑Map而在不得不使用对象的旧环境里请用数组来承担顺序这一职责。这份 jstips 第 32 期正是用最简短的示例把这一条极易被忽视的 JavaScript 陷阱讲透的典型范例。赞分享教程【免费下载链接】jstipsThis is about useful JS tips!项目地址https://gitcode.com/gh_mirrors/js/jstips点击查看免费下载相关推荐jstips 第 40 期解读掌握 JSON.stringify 的附加参数按需序列化对象属性jstips 第 40 期解读掌握 JSON.stringify 的附加参数按需序列化对象属性 JSON.stringify 是 JavaScript 中最教程Gleam OTP核心组件解析从Actor到Supervisor的完整指南 Gleam OTP核心组件解析从Actor到Supervisor的完整指南 如果你正在寻找构建 容错多核程序 的终极解决方案那么Gleam OTP绝对nlohmann/json 对象键顺序完全指南默认字典序机制与 ordered_json 插入序保序实战nlohmann/json 对象键顺序完全指南默认字典序机制与 ordered_json 插入序保序实战 本指南聚焦「JSON for Modern C」序列化创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考