分析JS對象內部屬性的遍歷順序

背景

在咱們平常的開發中,有不少下拉選框裏面的選項值是經過後端傳回來的枚舉值(enums)來做爲渲染的數據的。例如這樣子的下拉選框:
若是咱們須要讓下拉選框裏面的選項順序,是依據後端返回的數據進行排序,那這個時候就有兩種方法。一種是後端返回一個數組數據,而後咱們按照順序遍歷這個數據數據。但若是是後端返回的是這樣的一個對象,
那咱們就須要考慮一下Object.keys(), for ... in 等一系列的遍歷對象的方法可否按照後端屬性建立的順序來輸出了。javascript

問題重現

咱們先看一下下面這段代碼:java

var obj = {
      name: 'abc',
      3: 'ccc',
      age: 23,
      class: 'first',
      hobby: 'basketball'
};
console.log(Object.keys(obj));
// ["3", "name", "age", "class", "hobby"]
複製代碼

能夠看出,Object.keys()是沒法保證輸出的屬性的順序的。後端

Object.keys不保證對象屬性的順序?

MDN-Object.keys中描述是這樣的數組

Object.keys() returns an array whose elements are strings corresponding to the enumerable properties found directly upon object. The ordering of the properties is the same as that given by looping over the properties of the object manually.瀏覽器

說是和手動遍歷相同,那再看一下MDN-for..inmarkdown

Array indexes are just enumerable properties with integer names and are otherwise identical to general object properties. There is no guarantee that for...in will return the indexes in any particular order...Because the order of iteration is implementation-dependent.ide

最後一句表達了不一樣瀏覽器的實現方式是不一樣的。 但在一篇文章中描述Property order is predictable in JavaScript objects since ES2015
根據文章重的描述是 Object.getOwnPropertyNames, Reflect.ownKeys他們都是內部經過ownPropertyKeys這個方法實現的,並且在文章中他還給出了對象中屬性的輸出順序的規則oop

  1. 數字或者字符串類型的數字看成key時,輸出是按照升序排序的
  2. 普通的字符串類型的key,就按照定義的順序輸出
  3. Symbols也是和字符串類型的規則同樣
  4. 若是是三種類型的key都有,那麼順序是 1 -> 2 -> 3

Reflect.ownKeys()在IE中是不支持的 post

Object.getOwnPropertyNames()的兼容性更好 spa

但若是是想用Object.keys,這個是不是基於ownPropertyKeys方法,還要根據瀏覽器實現而不一樣。

標準參考

根據 ECMA-262(ECMAScript)第三版中描述,for-in 語句的屬性遍歷的順序是由對象定義時屬性的書寫順序決定的。

在現有最新的 ECMA-262(ECMAScript)第五版規範中,對 for-in 語句的遍歷機制又作了調整,屬性遍歷的順序是沒有被規定的。

通過上面問題重現的代碼復現以及查閱網上的信息,Chrome Opera 的 JavaScript 解析引擎遵循的是新版 ECMA-262 第五版規範。所以,使用 for-in 語句遍歷對象屬性時遍歷順序並不是屬性構建順序。而 IE6 IE7 IE8 Firefox Safari 的 JavaScript 解析引擎遵循的是較老的 ECMA-262 第三版規範,屬性遍歷順序由屬性構建的順序決定。

Chrome的Object.keys的輸出順序以及緣由分析

Chrome 的 JS 引擎遍歷對象屬性時會遵循一個規律:

它們會先提取全部 key 的 parseFloat 值爲非負整數的屬性,而後根據數字順序對屬性排序首先遍歷出來,而後按照對象定義的順序遍歷餘下的全部屬性。例以下面的輸出:

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

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

// [1, 2, 'b', 'a', '測']
複製代碼

Chrome有這樣的表現是由於V8引擎爲了提升對象的訪問速度,V8 裏的對象就維護兩個屬性,會把數字放入線性的 elements 屬性中,並按照順序存放。會把非數字的屬性放入 properties 中,不會排序。尋找屬性時先 elements 然後在 properties。V8引擎這樣作的緣由能夠在李兵老師的V8採用了哪些策略提高了對象屬性的訪問速度?中找到。

總結

咱們目前的項目是隻須要兼容Chrome瀏覽器便可,能夠看出只要是數組的返回值裏面,屬性值的key不是數字與字符串混合用,那麼徹底可使用Object.keys而且能夠保證讀取時的順序的,保證下拉選項渲染的順序,無需後端爲此改成數組返回,後端也省去了把以前數據和代碼所有更改的工做量和風險。

引用

爲何 JS 對象內部屬性遍歷的順序亂了
Object.keys(..)對象屬性的順序?
Property order is predictable in JavaScript objects since ES2015
js中關於for...in遍歷對象屬性的順序問題

相關文章
相關標籤/搜索