如果我创建这样的对象:
var obj = {};
obj.prop1 = "Foo";
obj.prop2 = "Bar";
生成的对象会始终看起来像这样吗?
{ prop1 : "Foo", prop2 : "Bar" }
也就是说,这些属性的顺序是否与我添加它们的顺序相同?
自 ES2015 起,对象的迭代顺序遵循一组特定的规则,但不(始终)遵循插入顺序。简而言之,迭代顺序是字符串键的插入顺序和数字键的升序的组合:
// key order: 1, foo, bar
const obj = { "foo": "foo", "1": "1", "bar": "bar" }
使用数组或Map
对象可能是实现此目的的更好方法。 Map
Object
有一些相似之处,并保证按插入顺序对键进行迭代,无一例外:
Map 中的键是有序的,而添加到对象的键则没有顺序。因此,在对其进行迭代时,Map 对象将按插入顺序返回键。 (请注意,在 ECMAScript 2015 规范中,对象确实保留了字符串和符号键的创建顺序,因此仅使用字符串键遍历对象即会按插入顺序产生键)
请注意,在 ES2015 之前根本无法保证对象中的属性顺序。 ECMAScript 第三版(pdf)中对象的定义:
4.3.3 对象
对象是对象类型的成员。它是属性的无序集合,每个属性都包含原始值,对象或函数。存储在对象属性中的函数称为方法。
是(用于非整数键)。
大多数浏览器将对象属性迭代为:
一些较旧的浏览器将类别#1 和#2 组合在一起,以插入顺序迭代所有键。如果您的键可能解析为整数,则最好不要依赖任何特定的迭代顺序。
保留当前语言规范(自 ES2015 起)的插入顺序,但键解析为整数(例如 “7” 或 “99”)的情况除外,在这种情况下,浏览器的行为会有所不同。例如,当键解析为数字时,Chrome / V8 不遵守插入顺序。
旧语言规范(ES2015 之前) :迭代顺序在技术上未定义,但所有主流浏览器均符合 ES2015 行为。
请注意,ES2015 行为是语言规范受现有行为驱动的一个很好的例子,而不是相反。要更深入地了解这种向后兼容的心态,请参阅http://code.google.com/p/v8/issues/detail?id=164 ,这是一个 Chrome 错误,其中详细介绍了 Chrome 迭代顺序行为背后的设计决策。在该错误报告中,每(有一些自以为是)评论:
标准始终遵循实现,这就是 XHR 的来历,而 Google 则通过实现 Gears 然后采用等效的 HTML5 功能来做同样的事情。正确的解决方法是让 ECMA 将事实上的标准行为正式纳入规范的下一个修订版。
普通对象中的属性顺序是 Java 语言中的一个复杂主题。
虽然在 ES5 中明确未指定任何顺序,但在某些情况下 ES2015 具有一个顺序。给定以下对象:
o = Object.create(null, {
m: {value: function() {}, enumerable: true},
"2": {value: "2", enumerable: true},
"b": {value: "b", enumerable: true},
0: {value: 0, enumerable: true},
[Symbol()]: {value: "sym", enumerable: true},
"1": {value: "1", enumerable: true},
"a": {value: "a", enumerable: true},
});
这导致以下顺序(在某些情况下):
Object {
0: 0,
1: "1",
2: "2",
b: "b",
a: "a",
m: function() {},
Symbol(): "sym"
}
因此,存在三个段,它们可能会更改插入顺序(如示例中所示)。而且类似整数的键根本不遵守插入顺序。
问题是,在 ES2015 规范中可以保证用哪种方法订购此顺序?
以下方法保证了显示的顺序:
以下方法 / 循环完全不保证顺序:
结论:即使在 ES2015 中,您也不应依赖 Javascript 中普通对象的属性顺序。容易出错。请改用Map
。