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

mel*_*oon 584 javascript object

如果我创建这样的对象:

var obj = {};
obj.prop1 = "Foo";
obj.prop2 = "Bar";
Run Code Online (Sandbox Code Playgroud)

生成的对象总是这样吗?

{ prop1 : "Foo", prop2 : "Bar" }
Run Code Online (Sandbox Code Playgroud)

也就是说,属性是否与我添加它们的顺序相同?

bpi*_*rre 429

不,JavaScript中不保证对象中的属性顺序; 你需要使用一个Map.

ECMAScript第三版中对象的定义(pdf):

4.3.3对象

对象是Object类型的成员.它是一个无序的属性集合,每个属性都包含一个原始值,对象或函数.存储在对象属性中的函数称为方法.

从ECMAScript 2015开始,使用Map对象可能是另一种选择.A Object与a 共享一些相似之处Map保证按键顺序:

Map以插入顺序迭代其元素,而未为Objects指定迭代顺序.

  • 这个答案在ES2015中是错误的. (35认同)
  • @BenjaminGruenbaum你必须对添加地图感到困惑吗?使用ES2015仍然以未指定的顺序迭代对象键,但Maps保证顺序.https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map#Objects_and_maps_compared (21认同)
  • 制作一对数组.`[["key1","value1"],["key2","value2"]]`或`[{"key1":"value1"},{"key2":"value2"}]`.把它包装在你自己的课堂上,问题解决了!`var o = new OrderedObject([{key1:"blah"}]); o.key1 ="我是一个用setter设置的值" (14认同)
  • @BenjaminGruenbaum确实!但也见9.1.11(http://www.ecma-international.org/ecma-262/6.0/index.html#sec-ordinary-object-internal-methods-and-internal-slots-enumerate):"The没有指定"枚举属性的机制和顺序".在此讨论https://esdiscuss.org/topic/property-ordering-of-enumerate-getownpropertynames (6认同)
  • @CyrilN.哦,不,你对另一个js陷阱感到困惑.数组也是对象.当你执行`a [3] ="val"`时,你插入数组,这显然是有序的(在索引3处).当你执行`a ['key1'] ="val"`或(等效)`a.key1 ="val"`时,你要向Object添加一个新属性,但不能保证对它进行排序.可以将其视为数组"元素"和对象"属性".数组对象具有两者,但它们不是同一个集合. (5认同)
  • 整数键的行为在所有浏览器中并不一致。一些较旧的浏览器按插入顺序(与字符串键)迭代整数键,有些按升序迭代。 (4认同)
  • 如前所述,答案实际上是错误的,并且附录可以使答案更加错误。引用的Mozilla页面不再包含引用的文本(这是有道理的,因为实际上是错误的)。答案是否可以不被接受或正确编辑? (3认同)
  • 正如Benjamin Gruenbaum所说,这个答案对于ES2015 +是不正确的(例如,任何甚至是半现代的JavaScript引擎).有关正确答案,请参阅[Oriol的答案](http://stackoverflow.com/a/30919039/157247). (2认同)
  • @DaveDopson - 正确 - 过时的浏览器不遵循当前规范,因为它们没有更新。 (2认同)

Dav*_*son 180

当前语言规范:从技术上讲,订单未指定.

当前浏览器:订单被保留,除了像"7"这样的键,其解析为整数并由Chrome/V8以不同方式处理.

未来语言规范(> ES2015):一般来说,你可以预期今天订购的东西不会变得无序.新API将保证订单; 现有的API很难改变.有关更多详细信息,请参阅JMM的答案.

下面的最佳链接是Tim Down的评论:

http://code.google.com/p/v8/issues/detail?id=164

该漏洞详细介绍了Chrome实施密钥排序所涉及的设计决策.一个外卖是,对于不解析为整数的字符串键(即"a"或"b",但不是"3"),键在所有主要浏览器上以插入顺序打印,而这种行为不是"标准化",它IS认为浏览器厂商一显著向后兼容性问题.使用风险由您自己承担.

每一个(相当自以为是)的评论:

标准总是遵循实现,这就是XHR的来源,而Google通过实施Gears然后拥抱等效的HTML5功能来做同样的事情.正确的解决方案是让ECMA正式将事实上的标准行为纳入规范的下一版本.

如果您依赖于插入顺序,那么您就不在ECMAScript规范之内,但只要您的密钥不作为整数解析,就在普通浏览器行为的事实标准范围内.

  • 这个答案在ES2015中是错误的.它是标准化的. (11认同)
  • @BenjaminGruenbaum您的评论是错误的.在ES2015中,仅对**选择的**方法保证订单.请参阅下面*ftor*的[answer](http://stackoverflow.com/a/38218582/95735). (5认同)
  • @BenjaminGruenbaum - 这完全是我的观点.截至2014年,所有主要供应商都有共同的实施,因此最终将遵循标准(即2015年). (2认同)
  • 顺便说一句:[React`createFragment` API](https://facebook.github.io/react/docs/create-fragment.html)已经依赖于此... (2认同)

小智 74

普通对象中的属性顺序是Javascript中的复杂主题.

虽然在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},
});
Run Code Online (Sandbox Code Playgroud)

这导致以下顺序(在某些情况下):

Object {
  0: 0,
  1: "1",
  2: "2",
  b: "b",
  a: "a",
  m: function() {},
  Symbol(): "sym"
}
Run Code Online (Sandbox Code Playgroud)
  1. 类似整数的键按升序排列
  2. 插入顺序中的普通键
  3. 插入顺序中的符号

因此,有三个段,可能会改变插入顺序(如示例中所示).类似整数的键根本不会影响插入顺序.

问题是,在ES2015规范中,该订单的保证方法是什么?

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

  • Object.assign
  • Object.defineProperties
  • Object.getOwnPropertyNames
  • Object.getOwnPropertySymbols
  • Reflect.ownKeys

以下方法/循环保证根本没有订单:

  • Object.keys
  • 的for..in
  • JSON.parse
  • JSON.stringify

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


Aln*_*tak 66

在撰写本文时,大多数浏览器确实以与插入时相同的顺序返回属性,但显然不能保证行为,因此不应该依赖它们.

ECMAScript规范常说的:

未指定枚举属性的机制和顺序.

但是在ES2015及更高版本中,将按插入顺序返回非整数键.

  • Chrome为其他浏览器实现了不同的订单.请参阅http://code.google.com/p/v8/issues/detail?id=164 (15认同)
  • Opera 10.50及以上版本以及IE9符合Chrome的订单.Firefox和Safari现在是少数(它们都使用不同的对象/阵列命令). (9认同)
  • 这个答案在ES2015中是错误的. (3认同)

JMM*_*JMM 42

整个答案是在规范合规的背景下,而不是任何引擎在特定时刻或历史上所做的事情.

一般来说,没有

实际问题非常模糊.

属性是否与我添加它们的顺序相同

在什么情况下?

答案是:它取决于许多因素.一般来说,没有.

有时候是

在这里您可以依赖普通的属性键顺序Objects:

  • 符合ES2015标准的引擎
  • 自己的财产
  • Object.getOwnPropertyNames(),Reflect.ownKeys(),Object.getOwnPropertySymbols(O)

在所有情况下,这些方法都包括非枚举属性键和指定键[[OwnPropertyKeys]](如下所示).它们包含的键值(String和/或Symbol)的类型不同.在此上下文中String包括整数值.

Object.getOwnPropertyNames(O)

返回O自己的String键属性(属性名称).

Reflect.ownKeys(O)

返回O自己的String- 和 - Symbol键的属性.

Object.getOwnPropertySymbols(O)

返回O自己的Symbol键控属性.

[[OwnPropertyKeys]]

顺序基本上是:Strings按升序排列的整数Strings,创建顺序中的非整数,创建顺序中的符号.根据哪个函数调用此函数,可能不包括其中一些类型.

特定语言是按以下顺序返回键:

  1. ... [正在迭代的对象]的每个自己的属性键P,O它是一个整数索引,按升序数字索引顺序排列

  2. ......每一个自己的财产关键PO是一个字符串,但不是整数指数,物业创建顺序

  3. ......每一个自己的财产关键PO是一个符号,在属性创建顺序

Map

如果您对有序地图感兴趣,您应该考虑使用MapES2015中引入的类型而不是普通的类型Objects.


Cer*_*nce 18

从 ES2015 开始,某些迭代属性的方法可以保证属性顺序。但不是其他人。不幸的是,不能保证有订单的方法通常是最常用的:

  • Object.keys, Object.values,Object.entries
  • for..in 循环
  • JSON.stringify

但是,从 ES2020 开始,由于已完成的提案:for-in 机制,规范保证这些以前不可信方法的属性顺序以与其他方法相同的确定性方式进行迭代。

就像具有保证迭代顺序的方法(如Reflect.ownKeysObject.getOwnPropertyNames)一样,先前未指定的方法也将按以下顺序迭代:

  • 数字数组键,按数字升序排列
  • 所有其他非符号键,按插入顺序
  • 符号键,按插入顺序

这几乎是每个实现都已经完成的(并且已经完成了很多年),但新提案已使其正式化。

尽管当前的规范保留......迭代顺序“几乎完全未指定,但实际引擎往往更加一致:”

ECMA-262 缺乏特异性并不能反映现实。在过去几年的讨论中,实现者已经观察到 for-in 的行为存在一些限制,任何想要在 Web 上运行代码的人都需要遵循这些限制。

因为每个实现都已经以可预测的方式迭代了属性,所以可以将其放入规范中而不会破坏向后兼容性。


有一些奇怪的情况,目前的实现同意,在这种情况下,结果的顺序将继续是未指定的。要保证财产秩序:

被迭代的对象及其原型链中的任何东西都不是代理、类型化数组、模块命名空间对象或宿主外来对象。

在迭代过程中,对象及其原型链中的任何东西都没有原型更改。

在迭代过程中,对象及其原型链中的任何内容都没有删除属性。

对象的原型链中没有任何东西在迭代期间添加属性。

在迭代过程中,对象或其原​​型链中的任何属性都不会改变其可枚举性。

没有不可枚举的属性会影响可枚举的属性。


Top*_*cus 13

在现代浏览器中,您可以使用Map数据结构而不是对象.

开发者mozilla>地图

Map对象可以按插入顺序迭代其元素...


Gre*_*Ros 8

在ES2015中,它可以,但不是您可能认为的

直到ES2015才保证对象中键的顺序。它是实现定义的。

但是,在ES2015中指定。像JavaScript中的许多事情一样,这样做是出于兼容性目的,并且通常反映了大多数JS引擎中的现有非官方标准(您知道,谁是例外)。

该顺序在规范中的抽​​象操作OrdinaryOwnPropertyKeys下定义,该操作支持对对象自己的键进行迭代的所有方法。解释如下,顺序如下:

  1. 所有整数索引键(这样的东西"1123""55"等),在上升的数字顺序。

  2. 所有不是整数索引的字符串键,按创建顺序(最早的顺序)。

  3. 所有符号键,按创建顺序(最早的顺序)。

说这个顺序不可靠是很愚蠢的-它是可靠的,可能不是您想要的,现代浏览器正确地实现了这个顺序。

某些例外情况包括枚举继承的键的方法,例如for .. in循环。将for .. in根据规范循环不保证秩序。


小智 5

正如其他人所说,当您迭代对象的属性时,您无法保证顺序.如果您需要多个字段的有序列表,我建议创建一个对象数组.

var myarr = [{somfield1: 'x', somefield2: 'y'},
{somfield1: 'a', somefield2: 'b'},
{somfield1: 'i', somefield2: 'j'}];
Run Code Online (Sandbox Code Playgroud)

这样,您可以使用常规for循环并具有插入顺序.然后,您可以使用Array排序方法将其排序为新数组(如果需要).


小智 5

刚刚发现这一点很困难。

将 React 与 Redux 结合使用,每次存储更改时,我想要遍历其键以生成子项的状态容器都会刷新(根据 Redux 的不变性概念)。

因此,为了采取Object.keys(valueFromStore)我使用的Object.keys(valueFromStore).sort(),这样我至少现在有按键的字母顺序。


小智 5

对象和 MAP 之间的主要区别(举例):

\n

它是循环中的迭代顺序,在 Map 中它遵循创建时设置的顺序,而在 OBJECT 中则不然。

\n

请参阅:\n对象

\n
const obj = {};\nobj.prop1 = "Foo";\nobj.prop2 = "Bar";\nobj['1'] = "day";\nconsole.log(obj)\n\n**OUTPUT: {1: "day", prop1: "Foo", prop2: "Bar"}**\n
Run Code Online (Sandbox Code Playgroud)\n

地图

\n
    const myMap = new Map()\n    // setting the values\n    myMap.set("foo", "value associated with 'a string'")\n    myMap.set("Bar", 'value associated with keyObj')\n    myMap.set("1", 'value associated with keyFunc')\n\nOUTPUT:\n**1.    \xe2\x96\xb60: Array[2]\n1.   0: "foo"\n2.   1: "value associated with 'a string'"\n2.  \xe2\x96\xb61: Array[2]\n1.   0: "Bar"\n2.   1: "value associated with keyObj"\n3.  \xe2\x96\xb62: Array[2]\n1.   0: "1"\n2.   1: "value associated with keyFunc"**\n
Run Code Online (Sandbox Code Playgroud)\n

  • 2020 年谁还在使用 var ? (6认同)