当键名具有数值时,JSON.parse()是否真的对属性进行排序?
Does JSON.parse() really sort properties when the key names have numeric values?
这里有很多关于这个问题的帖子,它们都包含很多断言,可以总结如下:
- 对象属性不保证以任何方式排序。
-
JSON.parse()
从不以任何方式排序属性。
显然,我们倾向于对上面的第一点毫无疑问,因此我们可以合理地期望,对于任何操作,属性都只是按照它们出现的顺序处理的。
由此我们可以特别推断出#2应该是正确的。
但是看看这个片段:
(BTW注意:为了显示结果,下面的代码片段不使用console.log()
,这可能会改变输出的顺序。相反,对象由for (key in obj)
迭代,并在文档中显示输出)
var inputs = [
'{"c": "C", "a": "A", "b": "B"}',
'{"3": "C", "1": "A", "2": "B"}',
'{"c": "C", "a": "A", "3": "C", "1": "A", "b": "B", "2": "B"}'
];
for (var i in inputs) {
var json = inputs[i],
parsed = JSON.parse(json),
output = [];
for (var j in parsed) {
output.push(j + ': ' + parsed[j]);
}
document.write(`JSON: ${json}<br />Parsed: ${output.join(', ')})<hr />`);
}
它显示,给定一个具有无序键的JSON字符串:
- 当输入具有非数值的键时,解析对象的属性的顺序与输入中的相同。这与上面的第二个假设是一致的。 相反,当输入具有具有数值的键时(尽管它们是字符串,因此不会引发解析错误),解析对象将其属性排序为。这现在与#2假设相矛盾。
- 更多:当有数字和非数字混合键值时,首先出现排序的数字属性,然后出现按原顺序排列的非数字属性。
我第一次试图得出结论,实际上会有一个(未记录的?)功能,所以JSON.parse()
遵循上面暴露的"规则"工作。
但是我有进一步研究的想法,所以下面的代码片段现在显示了仅仅编码对象的属性是如何排序的:
var objects = [
[
'{"c": "C", "a": "A", "b": "B"}',
{"c": "C", "a": "A", "b": "B"}
],
[
'{"3": "C", "1": "A", "2": "B"}',
{"3": "C", "1": "A", "2": "B"}
],
[
'{"c": "C", "a": "A", "3": "C", "1": "A", "b": "B", "2": "B"}',
{"c": "C", "a": "A", "3": "C", "1": "A", "b": "B", "2": "B"}
]
];
for (var i in objects) {
var object = objects[i],
output = [];
for (var j in object[1]) {
output.push(j + ': ' + object[1][j]);
}
document.write(`Code: ${object[0]}<br />Object: ${output.join(', ')}<hr />`);
}
它会产生类似的观察结果,即无论它们被编码的顺序是什么,属性都按照上面的第三条规则存储:
- 数字命名的属性都放在前面,排序
- 接下来设置其他属性,按编码 排序
所以这意味着JSON.parse()
没有参与:事实上,它似乎是对象构建的一个基本过程。
同样,这似乎没有记录,至少就我所能找到的而言。
有什么真正权威的规则吗?
[编辑,感谢@Oriol的回答]实际上,综合来看:
- 此行为符合ECMA规范规则。
- 此规则应适用于保证特定订单的所有方法,但对于其他情况是可选的。
- 然而,似乎现代浏览器都选择应用规则,无论方法涉及,因此明显的矛盾。
对象的属性没有顺序,所以JSON.parse
不能对它们排序。但是,当您列出或枚举对象的属性时,顺序可能是定义良好的,也可能不是。
对于for...in
循环和Object.keys
循环都不一定
在ES6中为对象属性引入了定义良好的枚举顺序吗?,规范说
未指定枚举属性的机制和顺序
但是对于OrdinaryOwnPropertyKeys
对象有一个内部的[[OwnPropertyKeys]]方法,例如Object.getOwnPropertyNames
和Object.getOwnPropertySymbols
使用这个方法。
对于普通对象,该方法使用OrdinaryGetOwnProperty抽象操作,该操作以定义良好的顺序返回属性:
当抽象操作OrdinaryOwnPropertyKeys被调用时对象O,则采取以下步骤:
- 让keys为新的空List。
- 对于O的每个属性键P是一个整数索引,在升序数字索引顺序
- 添加P作为键的最后一个元素。
- 对于O的每个属性键P,它是字符串但不是一个整数索引,按创建属性的升序排列
- 添加P作为键的最后一个元素。
- 对于O的每个属性键P,它是一个符号,按属性创建的升序排列
- 添加P作为键的最后一个元素。
- 键返回。
因此,由于OrdinaryOwnPropertyKeys需要一个顺序,实现可能决定在内部以该顺序存储属性,并在枚举时也使用它。这是你观察到的,但你不能依赖它。
也要注意非普通对象(例如代理对象)可能有另一个[[OwnPropertyKeys]]内部方法,所以即使使用Object.getOwnPropertyNames
顺序仍然可能不同。
,因此我们可以合理地预期,对于任何操作,属性仅仅按照它们出现的顺序进行处理
这就是推理的缺陷所在。如果不能保证对象属性是有序的,我们必须假设任何操作都以它认为合适的顺序处理属性。
事实上,引擎在某种程度上对整数属性进行了特殊处理——它们就像数组索引,并且以比查找表更有效的格式存储。
- 访问布局信息是否也会导致浏览器重排
- 当包含另一个asp文件时,是否也包含所有引用的样式和脚本页面
- 使用js/jQuery检查对象(而不是元素)是否真的存在
- 有没有办法检测用户是否真的远离计算机
- 检查数组位置是否真的未定义
- 对象构造:原型是否真的有必要
- res.writehead是否真的写到我的html页面的头部
- 检测 Javascript 变量是否真的未定义
- AngularJS:判断没有(各种类型的)输入是否为真的最简单方法
- 如何检查这个由空 json 输出导致的空对象是否真的为空
- 我怎么知道谷歌脚本是否真的在运行
- 检查是否设置了cookie以及是否真的启动功能
- 如何判断播放HTML5音频是否真的是在制造声音
- Fullcalendar 2是否与Bootstrap 3和Rails 4不兼容(我真的需要帮助……在这个问题上)
- 当键名具有数值时,JSON.parse()是否真的对属性进行排序?
- Angular 2 -验证一个文件是否真的是一个图像
- 选择.调用是否真的返回 d3 中的选择
- 我真的需要用jQuery检查元素是否存在吗
- 如何判断一个文件是否真的被下载和保存,尽管浏览器预取/缓存
- 在JavaScript中读取数组的' length '属性是否真的是一个昂贵的操作