常見的誤解–“無法保證
JavaScript
屬性的順序”:幾年前開始編寫JavaScript
時,您可能已經聽說過JS對象中屬性的順序是不可預測的。我從來沒有碰到過奇怪和不尋常的屬性順序,但是我始終遵循從不依賴屬性順序
的原則。
內部ownPropertyKeys
方法
事實上,自ES6
起,有一些方法基於定義屬性順序的特定規則,除了一種特殊情況外,該順序是按時間順序排列的,並依賴於對象屬性的類型和值。
這些規則被定義在 the inernal “ownProertyKes” method 規範中。例如,es6
中新的函數Object.getOwnPropertyNames
和Reflect.ownKeys
已經使用了這些規則。
這個規則引起了一些有趣的變化,例如Object.keys
從ES5到ES6的規則發生了變化。ES6規範規定Object.keys
也依賴於ownPropertyKeys
,這樣就使得Object.keys
在現代瀏覽器中的執行結果也是可預測。
但是,這也意味着您必須謹慎使用此方法,並且不應依賴於可預測的順序使用Object.keys
,因爲Object.keys
的執行結果可能會因瀏覽器的實現而有所不同。
但是已經有足夠多的理由,讓我們去了解ownPropertyKeys
是如何定義的屬性順序的。
1.整數指數
所有爲整數索引的屬性均按整體對象屬性順序顯示在第一位,並按數字升序排列。
const objWithIndices = {
23: 23,
'1': 1,
1000: 1000
};
console.log(Reflect.ownKeys(objWithIndices));
// [1, 23, 1000]
// ☝️ following numeric order
2.字符串(不是整數)
屬性既不是整數索引,也不是Symbol
類型的,按照添加的時間順序排列。
const objWithStrings = {
'bar': 'bar',
'01': '01'
};
objWithStrings.last = 'last';
objWithStrings['veryLast'] = 'veryLast';
console.log(Reflect.ownKeys(objWithStrings));
// ['bar', '01', 'last', 'veryLast']
// ☝️ following chronological order
3.Symbol
最後,Symbol
也按照時間順序排列。
const objWithSymbols = {
[Symbol('first')]: 'first',
[Symbol('second')]: 'second'
};
objWithSymbols[Symbol('last')] = 'last';
console.log(Reflect.ownKeys(objWithSymbols));
// [Symbol(first), Symbol(second), Symbol(last)]
// ☝️ following chronological order
三種類型一起
將三種類型的屬性放在一起時,整數始終位於對象屬性的“第一行”中,其後是字符串和Symbol
,此外,我們可以控制字符串和Symbol屬性的順序,因爲它們是按添加的時間順序排列!
const obj = {
'2': 'integer: 2',
'foo': 'string: foo',
'01': 'string: 01',
1: 'integer: 1',
[Symbol('first')]: 'symbol: first'
};
obj['0'] = '0';
obj[Symbol('last')] = 'symbol: last';
obj['veryLast'] = 'string: very last';
console.log(Reflect.ownKeys(obj));
// [ "0", "1", "2", "foo", "01", "veryLast", Symbol(first), Symbol(last) ]
// -> 1. integers in numeric order
// -> 2. strings in chronological order
// -> 3. Symbols in chronological order
編輯:正如Malgosia Stepniak所指出的那樣,僅現代瀏覽器完全支持 “own property order” ,而IE則不完全支持。