為什麼Object.keys的返回值會自動排序?

例子是這樣的:

[/code]而下面這例子又不自動排序了?

[code]

複製程式碼

查了查ECMA262規範,再加上後來看了看這方面的文章,明白了為什麼會發生這麼詭異的事情。

故此寫下這篇文章詳細介紹,當Object。keys被呼叫時內部都發生了什麼。

1。 答案

對於上面那個問題先給出結論,Object。keys在內部會根據屬性名key的型別進行不同的排序邏輯。分三種情況:

如果屬性名的型別是Number,那麼Object。keys返回值是按照key從小到大排序如果屬性名的型別是String,那麼Object。keys返回值是按照屬性被建立的時間升序排序。如果屬性名的型別是Symbol,那麼邏輯同String相同

這就解釋了上面的問題。

下面我們詳細介紹Object。keys被呼叫時,背後發生了什麼。

2。 當Object。keys被呼叫時背後發生了什麼

當Object。keys函式使用引數O呼叫時,會執行以下步驟:

第一步:將引數轉換成Object型別的物件。

第二步:透過轉換後的物件獲得屬性列表properties。

注意:屬性列表properties為List型別(List型別是ECMAScript規範型別)

第三步:將List型別的屬性列表properties轉換為Array得到最終的結果。

規範中是這樣定義的:

呼叫ToObject(O)將結果賦值給變數obj呼叫EnumerableOwnPropertyNames(obj, ";key";)將結果賦值給變數nameList呼叫CreateArrayFromList(nameList)得到最終的結果

2。1 將引數轉換成Object(ToObject(O))

ToObject操作根據下表將引數O轉換為Object型別的值:

引數型別 | 結果 |

————————|————————————|

Undefined | 丟擲TypeError |

Null | 丟擲TypeError |

Boolean | 返回一個新的 Boolean 物件 |

Number | 返回一個新的 Number 物件 |

String | 返回一個新的 String 物件 |

Symbol | 返回一個新的 Symbol 物件 |

Object | 直接將Object返回 |

因為Object。keys內部有ToObject操作,所以Object。keys其實還可以接收其他型別的引數。

上表詳細描述了不同型別的引數將如何轉換成Object型別。

我們可以簡單寫幾個例子試一試:

先試試null會不會報錯:

徹底理解Object.keys()

圖1 Object。keys(null)

如圖1所示,果然報錯了。

接下來我們試試數字的效果:

徹底理解Object.keys()

圖2 Object。keys(123)

如圖2所示,返回空陣列。

為什麼會返回空陣列?請看圖3:

徹底理解Object.keys()

圖3 new Number(123)

如圖3所示,返回的物件沒有任何可提取的屬性,所以返回空陣列也是正常的。

然後我們再試一下String的效果:

徹底理解Object.keys()

圖4 Object。keys(‘我是Berwin’)

圖4我們會發現返回了一些字串型別的數字,這是因為String物件有可提取的屬性,看如圖5:

徹底理解Object.keys()

圖5 new String(‘我是Berwin’)

因為String物件有可提取的屬性,所以將String物件的屬性名都提取出來變成了列表返回出去了。

2。2 獲得屬性列表(EnumerableOwnPropertyNames(obj, ";key";))

獲取屬性列表的過程有很多細節,其中比較重要的是呼叫物件的內部方法OwnPropertyKeys獲得物件的ownKeys。

注意:這時的ownKeys型別是List型別,只用於內部實現

然後宣告變數properties,型別也是List型別,並迴圈ownKeys將每個元素新增到properties列表中。

最終將properties返回。

您可能會感覺到奇怪,ownKeys已經是結果了為什麼還要迴圈一遍將列表中的元素放到properties中。

這是因為EnumerableOwnPropertyNames操作不只是給Object。keys這一個API用,它內部還有一些其他操作,只是Object。keys這個API沒有使用到,所以看起來這一步很多餘。

所以針對Object。keys這個API來說,獲取屬性列表中最重要的是呼叫了內部方法OwnPropertyKeys得到ownKeys。

其實也正是內部方法OwnPropertyKeys決定了屬性的順序。

關於OwnPropertyKeys方法ECMA-262中是這樣描述的:

當O的內部方法OwnPropertyKeys被呼叫時,執行以下步驟(其實就一步):

Return ! OrdinaryOwnPropertyKeys(O)。

而OrdinaryOwnPropertyKeys是這樣規定的:

宣告變數keys值為一個空列表(List型別)把每個Number型別的屬性,按數值大小升序排序,並依次新增到keys中把每個String型別的屬性,按建立時間升序排序,並依次新增到keys中把每個Symbol型別的屬性,按建立時間升序排序,並依次新增到keys中將keys返回(return keys)

上面這個規則不光規定了不同型別的返回順序,還規定了如果物件的屬性型別是數字,字元與Symbol混合的,那麼返回順序永遠是數字在前,然後是字串,最後是Symbol。

舉個例子:

[code][/code]屬性的順序規則中雖然規定了Symbol的順序,但其實Object。keys最終會將Symbol型別的屬性過濾出去。(原因是順序規則不只是給Object。keys一個API使用,它是一個通用的規則)

2。3 將List型別轉換為Array得到最終結果(CreateArrayFromList( elements ))

現在我們已經得到了一個物件的屬性列表,最後一步是將List型別的屬性列表轉換成Array型別。

將List型別的屬性列表轉換成Array型別非常簡單:

先宣告一個變數array,值是一個空陣列迴圈屬性列表,將每個元素新增到array中將array返回

3。 該順序規則還適用於其他API

上面介紹的排序規則同樣適用於下列API:

Object。entriesObject。valuesfor。。。in迴圈Object。getOwnPropertyNamesReflect。ownKeys

注意:以上API除了Reflect。ownKeys之外,其他API均會將Symbol型別的屬性過濾掉。

文章來自:5分鐘徹底理解Object。keys - IT程式設計師 翁筆