问题的发现:
我有一个接口返回一个json数据浏览器network里的Response里是从大到小排。
但Preview就是反过来的
问题的描述:
上面那个让我发现浏览器处理对象或者json是会对其键值对做排序!!!
在JavaScript中,对象键的顺序问题源于语言规范:ES6之前对象键序没有明确规定,ES6之后虽然规定了自有属性的枚举顺序,但控制台输出可能因浏览器实现而异。
您观察到的现象是正常的:当使用console.log(data)时,浏览器控制台可能会按照以下规则显示对象:
数字键按数值升序排列
字符串键按创建顺序排列
但某些浏览器控制台(如Chrome)会对所有键进行字母排序后再显示
问题的复现:
示例一:key是一个字符串
let obj = {"2023-08-30": "8888","2023-08-10": "5555","2023-08-08": "6666","2023-06-02": "7777","2023-06-01": "7777",};// let obj = {// "3-3": "cc",// "3-2": "bb",// "3-1": "aa",// };console.log("原数据:",obj);console.log("key数组:", Object.keys(obj));
结果分析:
1.打印出的对象和原数据不一致被正序排序了
2.使用for in或者 Object.keys时键值的顺序没有发生变化
示例二:key是一个数字
let obj = {20230830: "8888",20230810: "5555",20230808: "6666",20230602: "7777",20230601: "7777",};console.log("原数据:", obj);console.log("key数组:", Object.keys(obj));
结果分析:
1.打印出的对象和原数据不一致被正序排序了
2.使用for in或者 Object.keys时键值的顺序发生变化了
示例三:key是一个字母
let obj = {c: "cc",b: "bb",a: "aa",};// let obj = {// "c": "cc",// "b": "bb",// "a": "aa",// };console.log("原数据:", obj);console.log("key数组:", Object.keys(obj));
结果分析:
1.打印出的对象和原数据不一致被正序排序了
2.使用for in或者 Object.keys时键值的顺序没有发生变化
总结:
经过上面的三个示例可得出:
数字字符串键在对象属性遍历时会按数值升序排列。
非数字字符串键按照创建顺序排列。
使用Map可以完全保留插入顺序。
需要注意的是JSON.parse也使用这个规则!
关键结论:
对象键序问题原因:
控制台显示可能对键进行字母排序
JavaScript规范中对象键的顺序是:整数索引 > 字符串键(按创建顺序)> Symbol键
但字符串键中的数字字符串会被特殊处理
特殊处理的规则如下:
具体来说,ECMAScript规范规定,对象中的数字键(即数字字符串键)会按照数值大小升序排序,而非数字字符串键则按照创建顺序排列。
特殊处理规则:
数字字符串键:会被视为数字索引,在对象属性遍历时(如使用Object.keys、for…in循环等)会按照数值升序排列。
非数字字符串键:按照属性添加到对象的先后顺序排列(从ES6开始,普通对象保持字符串键的创建顺序,但数字字符串键仍然会被特殊排序)。
const obj = {"100": "一百","2": "二","name": "张三","1": "一","age": 20
};console.log(Object.keys(obj));
// 输出: [ '1', '2', '100', 'name', 'age' ]
注意:
只有看起来像非负整数的字符串(例如"123"、“0”)才会被当作数字键处理。像"001"、"123a"这样的字符串不会被当作数字键,而是普通字符串键。
在ES6之前,不同JavaScript引擎对非数字字符串键的遍历顺序是不一致的。但从ES6开始,规范要求非数字字符串键按照创建顺序排列,而数字字符串键按照数值升序排列。
是否会影响使用:
直接通过键访问属性:不受影响
遍历对象属性(for…in/Object.keys):顺序可能不符合预期
依赖对象键序的业务逻辑:可能有风险
推荐解决方案:
使用Map对象保持插入顺序
使用数组显式存储键的顺序
在遍历前对键进行排序(如按日期倒序)
还需要注意的是控制台打印对象时可能也会出现不一致的情况,详情可以看:js 控制台打印对象,对象点开后里面属性没值,展开后有值(object展开和收起值不一致)