10

为什么 JS 对象内部属性遍历的顺序乱了

 4 years ago
source link: https://blog.souche.com/wei-shi-yao-js-dui-xiang-nei-bu-shu-xing-bian-li-de-shun-xu-luan-liao/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

问题重现

需求是要获取一个车型列表,并且输出到页面上按年份排序,故而接口提供的对象简化如下

let obj = {  
    '2018': {
        modelCode: "204313",
        modelName: "2018款 Vanquish 6.0L S Coupe"
    },
    '2017': {
        modelCode: "202479",
        modelName: "2017款 Rapide 6.0L AMR"
    },
    '2013': {
        modelCode: "139705",
        modelName: "2013款  Rapide  6.0L S"
    }
}

console.log(obj)  
// {2013: {…}, 2017: {…}, 2018: {…}}

??? 为什么 2013 在前面了,用户肯定希望先看到新的车型的,这不科学!

解释

查阅了 ECMA-262 3rd edition ,如下文 It is an unordered collection of properties 就说到 ES3 标准的对象不排序,插入是啥顺序,遍历就是啥顺序

An object is a member of the type Object. It is an unordered collection of properties each of which contains a primitive value, object, or function. A function stored in a property of an object is called a method.

而我查阅了 ECMA-262 5.1 edition ,如下文,读者们应该留意到了少了 unordered collection 的描述。并且之后的 ES 版本对对象的描述都是如此。

an object is a member of the remaining built-in type Object; and a function is a callable object. A function that is associated with an object via a property is a method.

故此,我能得出结论 Chrome 等新版浏览器 JS 引擎遵循的是新版 ECMA-262 5th。因此,使用 for-in 语句遍历对象属性时遍历书序并非属性构建顺序。而 IE6、7、8 等旧版本浏览器的 JS 解析引擎遵循的是较老的 ECMA-262 3rd,属性遍历顺序由属性构建的顺序决定。

故此 Chrome 的 JS 引擎遍历对象属性时会遵循一个规律:

它们会先提取所有 key 的 parseFloat 值为非负整数的属性,然后根据数字顺序对属性排序首先遍历出来,然后按照对象定义的顺序遍历余下的所有属性。

猜想

按照上面的解释,那么我来一个例子

let obj = {  
    'b': 'testb',
    'a': 'testa',
    '1': 'test1',
    '测': 'test测',
    '2': 'test2'
}

console.log(Object.keys(obj));

// [1, 2, 'b', 'a', '测']

果然会把 '1' 和 '2' 这种能被 parseFloat 转化为正整数的提到前面并且按照升序排

而 'a' 和 '测' 没法转为整数那就排在 '1'、'2' 后并按照构建时的顺序拍

解决问题

回到问题,对象既然不能保证其顺序,那么使用数组来进行遍历吧。

当然业务中如果需要查某个年份的车型,而不想要每次都遍历一遍的来找的话。可以维护两份数据。一份数组,用于遍历输出,一份对象,用于查。

补充

直到最近在极客时间翻阅到李兵老师的 图解 Google V8 的第三节 "V8采用了哪些策略提升了对象属性的访问速度?" 时,我终于发现了更深层次的解释。

借下图说法:V8 里的对象其实维护两个属性,会把数字放入线性的 elements 属性中,并按照顺序存放。会把非数字的属性放入 properties 中,不会排序,顺便说一句它可能是线性结构,取决于属性数量的多少。寻找属性时先 elements 而后在 properties。

aAVF7jm.jpg!web


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK