协慌网

登录 贡献 社区

JavaScript 是否保证对象属性顺序?

如果我创建这样的对象:

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. 升序的整数键(以及像 “1” 这样的字符串解析为整数)
  2. 字符串键,按插入顺序排列(ES2015 保证这样做,并且所有浏览器都遵守)
  3. 符号名称,按插入顺序排列(ES2015 保证此名称,并且所有浏览器都遵守)

一些较旧的浏览器将类别#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"
}
  1. 类整数键(升序)
  2. 普通键插入顺序
  3. 插入顺序中的符号

因此,存在三个段,它们可能会更改插入顺序(如示例中所示)。而且类似整数的键根本不遵守插入顺序。

问题是,在 ES2015 规范中可以保证用哪种方法订购此顺序?

以下方法保证了显示的顺序:

  • 对象分配
  • Object.defineProperties
  • Object.getOwnPropertyNames
  • Object.getOwnPropertySymbols
  • Reflect.ownKeys

以下方法 / 循环完全不保证顺序:

  • 对象键
  • 为.. 在
  • JSON.parse
  • JSON.stringify

结论:即使在 ES2015 中,您也不应依赖 Javascript 中普通对象的属性顺序。容易出错。请改用Map