背景
最近开发业务遇到一个需求:
产品希望把公司按照他希望的顺序排列,我们当前的实现逻辑是:
1 | const sectionMapToArray = (Object) => { |
很简单的一个循环操作,但是它引发了什么问题呢?
1 | const object = { |
我们看到输出的顺序变了,理想状态下是输出 ‘010101’、’102’、’101’、‘A01’
- 猜想一 按照 key 的 ASC 码
1 | const object = { |
对象的遍历输出并不是按照属性的 ASC 码升序排序。
- 猜想二 按照 key 转换成整数的大小
1 | const object = { |
我们在再看开头的例子好像是按照 key 的整数大小,测试一下:
1 | const object = { |
看结果 -100 应该是第一个但是并没有,在猜测一下是非负数整数:
1 | const object = { |
看结果似乎似乎接近真相了,我们在试试:
1 | const object = { |
我们发现 ‘010101’、’0101’ 这两个位置并不对,但是到这里已经没有什么头绪只能去查资料。
排序机制
integer properties are sorted, others appear in creation order.
通过查阅资料发现这么定义:当 key 整数类型会做一层排序,其他类型则按创建顺序来排。
但是我们通过最后例子可以发现 ‘010101’、’0101’ 转换成 number 后并不符合,所以如何定义 integer 整数?
通过上 integer 关键词在 查找:
An integer index is a String-valued property key that is a canonical numeric String (see 7.1.16) and whose numeric value is either +0 or a positive integer ≤ 2^53−1. An array index is an integer index whose numeric value i is in the range +0 ≤ i < 2^32−1.
我们发现canonical numeric String (see 7.1.16)有定义 integer :
The abstract operation CanonicalNumericIndexString returns argument converted to a numeric value if it is a String representation of a Number that would be produced by ToString, or the string “-0”. Otherwise, it returns undefined. This abstract operation functions as follows:
1.Assert: Type(argument) is String.
2.If argument is “-0”, return −0.
3.Let n be ToNumber(argument).
4.If SameValue(ToString(n), argument) is false, return undefined.
5.Return n.
A canonical numeric string is any String value for which the CanonicalNumericIndexString abstract operation does not return undefined.>
通过 7.1.16 我们知道会把 key 转换成数字,由于转换数字过程,第四条规则:
SameValue(ToString(n), argument)
我们知道 SameValue 是 Object.is() 内部采用的比较算法类似于 ===,区别保证 -0 和 +0 不再相同,但 Object.is(NaN, NaN) 会返回 true。
所以也就出现了 SameValue(TOString(10101),’010101’) 为 false 的情况就解释了
1 | const object = { |
‘010101’、’0101’ 的顺序问题
2^32−1
我们回过来在看文档中 2^32−1 代表什么意思,在看一个例子:
1 | const object = { |
我们发现 10188988888 在 010101 的前面,按照之前的逻辑应该是反着来的,所以就要介绍下 2^32−1:
32 位无符号整数的最大值。
为什么会有 2^32−1 边界限制,这里又涉及到 V8 的排序实现,大概意思是因为性能原因 详见:从 Chrome 源码看 JS Object 的实现
总结
当 key 整数类型会做一层排序,其他类型则按创建顺序来排,关于什么是整数类型要同时满足 canonical numeric String 和 +0 ≤ i < 2^32−1