為什么 JS 對象內部屬性遍歷的順序亂了


問題重現

需求是要獲取一個車型列表,並且輸出到頁面上按年份排序,故而接口提供的對象簡化如下

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 值為非負整數的屬性,然后根據數字順序對屬性排序首先遍歷出來,然后按照對象定義的順序遍歷余下的所有屬性。

廣州設計公司https://www.houdianzi.com 我的007辦公資源網站https://www.wode007.com

猜想

按照上面的解釋,那么我來一個例子

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。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM