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

JavaScript是否保证对象属性顺序?,javascript,object,Javascript,Object,如果我创建这样的对象: var obj = {}; obj.prop1 = "Foo"; obj.prop2 = "Bar"; 生成的对象总是这样吗 { prop1 : "Foo", prop2 : "Bar" } 也就是说,属性的顺序是否与我添加它们的顺序相同?在撰写本文时,大多数浏览器返回属性的顺序与插入属性的顺序相同,但这显然不能保证行为,因此不应依赖于此 过去人们常说: 枚举属性的机制和顺序。。。未指定 但是,在ES2015及更高版本中,非整数键将以插入顺序返回。对象的迭代顺序自ES

如果我创建这样的对象:

var obj = {};
obj.prop1 = "Foo";
obj.prop2 = "Bar";
生成的对象总是这样吗

{ prop1 : "Foo", prop2 : "Bar" }

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

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

过去人们常说:

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


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

对象的迭代顺序自ES2015以来一直遵循,但并不总是遵循插入顺序。简单地说,迭代顺序是字符串键的插入顺序和数字键的升序的组合:

// key order: 1, foo, bar
const obj = { "foo": "foo", "1": "1", "bar": "bar" }
使用数组或数组可以更好地实现这一点。Map与Object有一些相似之处,毫无例外:

贴图中的键是有序的,而添加到对象中的键不是有序的。因此,当对其进行迭代时,贴图对象将按插入顺序返回关键帧。请注意,在ECMAScript 2015规范中,对象确实保留了字符串键和符号键的创建顺序,因此仅使用ie字符串键遍历对象将按插入顺序生成键

请注意,在ES2015之前,对象中的属性顺序根本无法保证。对象的定义来自:

4.3.3目标 对象是 输入对象。它是一个无序的属性集合,每个属性 包含基元值、对象或 作用存储在数据库中的函数 对象的属性称为 方法

从:

对象是零个或多个名称/值对的无序集合,其中名称是字符串,值是字符串、数字、布尔值、null、对象或数组

我的

因此,不,您不能保证顺序。

对于非整数键,是

大多数浏览器将对象属性迭代为:

按升序排列的整数键和像1这样解析为整数的字符串 按插入顺序排列的字符串键ES2015保证了这一点和所有浏览器都符合 按插入顺序排列的符号名称ES2015保证这一点和所有浏览器都符合 一些较旧的浏览器结合了类别1和类别2,以插入顺序迭代所有键。如果您的键可能解析为整数,那么最好不要依赖任何特定的迭代顺序

自ES2015以来的当前语言规范将保留插入顺序,但解析为整数(如7或99)的键除外,其行为因浏览器而异。例如,当键解析为数字时,Chrome/V8不遵守插入顺序

ES2015之前的旧语言规范:迭代顺序在技术上未定义,但所有主要浏览器都遵循ES2015行为

请注意,ES2015行为是由现有行为驱动的语言规范的一个很好的例子,而不是相反。为了更深入地了解这种向后兼容性思维,请参阅一个Chrome bug,它详细介绍了Chrome迭代顺序行为背后的设计决策。 根据对该bug报告的一个相当固执己见的评论:

标准总是遵循实现,这就是XHR的来源,而谷歌也通过实现Gears,然后采用等价的HTML5功能来做同样的事情。正确的解决方法是让ECMA将事实上的标准行为正式纳入规范的下一版本


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

var myarr = [{somfield1: 'x', somefield2: 'y'},
{somfield1: 'a', somefield2: 'b'},
{somfield1: 'i', somefield2: 'j'}];

通过这种方式,您可以使用常规for循环并具有插入顺序。然后,如果需要,您可以使用数组排序方法将其排序为新数组。

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

贴图对象可以按插入顺序迭代其元素


整个答案是在符合规范的情况下给出的,而不是任何引擎在特定时刻或历史上所做的

一般来说,没有 实际问题非常模糊

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

在什么情况下

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

有时候,是的 在这里,您可以使用普通对象的属性键顺序:

符合ES2015标准的发动机 自有财产 Object.getOwnPropertyNames、Reflect.ownKeys、Object.getOwnPropertySymbolsO 在所有情况下,这些方法都包括[[OwnPropertyKeys]]指定的不可枚举属性键和顺序键,请参见下文。它们包括字符串和/或符号的键值类型不同。在此上下文中,字符串包括整数值

返回O自己的字符串键控属性属性名

返回O自己的字符串和符号键控属性

返回O自己的符号键控pr 不动产

顺序基本上是:按升序排列的类似整数的字符串,按创建顺序排列的类似非整数的字符串,按创建顺序排列的符号。根据调用此函数的函数,可能不包括其中的某些类型

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

。。。每个对象都拥有O[正在迭代的对象]的属性键P,它是一个整数索引,以升序数字索引顺序排列

。。。每个属性都拥有O的属性键P,它是一个字符串,但不是一个整数索引,按属性创建顺序

。。。每个属性都拥有作为符号的O的属性键P,以属性创建顺序排列

地图

如果您对有序映射感兴趣,您应该考虑使用ES2015中引入的映射类型代替普通对象。 虽然在ES5中明确未指定订单,但ES2015在某些情况下定义了订单,此后对规范的连续更改越来越多地定义了订单,甚至在ES2020中,for in循环的订单也是如此。给定的是以下对象:

const 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中,只有某些方法遵循以下顺序:

Object.assign Object.defineProperties Object.getOwnPropertyNames Object.getOwnPropertySymbols 反思自己的钥匙 JSON.parse JSON.stringify 从ES2020开始,所有其他项目都在ES2015和ES2020之间的规范中进行,其他项目在ES2020中进行,包括:

Object.keys、Object.entries、Object.values等。。。 为了……在 最难确定的是in,因为它独特地包含了继承财产。在ES2020中,除了边缘以外的所有情况下都是如此。链接的已完成提案中的以下列表提供了未指定订单的边缘情况:

被迭代的对象或其原型链中的任何对象都不是代理、类型化数组、模块名称空间对象或主机对象。 无论是对象还是其原型链中的任何东西,在迭代过程中都不会改变其原型。 在迭代过程中,对象及其原型链中的任何对象都没有删除属性。 在迭代过程中,对象的原型链中没有添加任何属性。 在迭代过程中,对象的属性或其原型链中的任何内容的枚举性都不会改变。 没有不可枚举属性隐藏可枚举属性。
结论:即使在ES2015中,也不应该依赖JavaScript中普通对象的属性顺序。它容易出错。如果您需要有序的命名对,请改用,这纯粹是使用插入顺序。如果您只需要顺序,请使用数组或也使用纯插入顺序的数组。

刚刚通过艰难的途径找到了这一点

使用React with Redux,每次根据Redux的不变性概念更改存储时,我都会刷新状态容器中的键,以便生成子项

因此,为了获取Object.keysvalueFromStore,我使用了Object.keysvalueFromStore.sort,这样我至少现在有了一个键的字母顺序。

在ES2015中,它是这样的,但与您可能认为的不同 直到ES2015,对象中键的顺序才得到保证。它是实现定义的

然而,在ES2015中,规定了。像JavaScript中的许多事情一样,这是出于兼容性目的而做的,通常反映了大多数JS引擎中存在的一个非官方标准,您知道谁是例外

顺序在规范中的抽象操作下定义,它支持所有迭代对象自身键的方法。其顺序如下:

所有整数索引键都是按升序排列的,比如1123、55等

所有不是整数索引的字符串键,按创建顺序排列

所有符号键,按先创建的顺序排列

说顺序不可靠是愚蠢的——它是可靠的,它可能不是你想要的,而现代浏览器正确地实现了这个顺序


某些例外情况包括枚举继承键的方法,例如for。。循环中。为…准备的。。in-loop不保证按照规范订购

从ES2015开始,对于某些迭代属性的方法,属性顺序是有保证的。不幸的是,不保证有订单的方法通常是最常用的:

Object.keys、Object.values、Object.entries for..在循环中 JSON.stringify 但是,从ES2020开始,由于t 他建议:

与具有保证迭代顺序的方法(如Reflect.ownKeys和Object.getOwnPropertyNames)一样,以前未指定的方法也将按以下顺序迭代:

数字数组键,按升序排列 按插入顺序排列的所有其他非符号键 符号键,按插入顺序 这是很多年来几乎所有实施都已经做过的事情,但新的提案已经正式提出

尽管目前的规范以迭代顺序为..,但实际引擎往往更为一致:

ECMA-262缺乏特异性并不反映实际情况。在多年前的讨论中,实现者注意到for的行为存在一些限制,任何想要在web上运行代码的人都需要遵守这些限制

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

有一些奇怪的情况,实现目前不同意,在这种情况下,结果顺序将继续不明。物业订单:

被迭代的对象或其原型链中的任何对象都不是代理、类型化数组、模块名称空间对象或主机对象

无论是对象还是其原型链中的任何东西,在迭代过程中都不会改变其原型

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

在迭代过程中,对象的原型链中没有添加任何属性

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

没有不可枚举属性隐藏可枚举属性


对象和贴图之间的主要区别,例如:

它是循环中的迭代顺序,在映射中它遵循创建时设置的顺序,而在对象中则不遵循

见: 反对

地图


这是由ECMAScript标准指定的,而不是JSON规范。@Alnitak、@Iacqui:JSON仅从ECMAScript规范中获取。它也是为JSON指定的,但这实际上与问题无关。Chrome实现了与其他浏览器不同的顺序。参见Opera 10.50及以上版本,以及IE9,符合Chrome的订单。Firefox和Safari现在只占少数,它们对对象/数组使用不同的顺序。@Veverke没有明确的顺序保证,因此人们应该始终假设顺序实际上是随机的。@Veverke没有,顺序完全不是可预测的。它依赖于实现,随时可能更改,并且可能会更改,例如每次浏览器更新时。此答案在ES2015中是错误的。也可能重复相关的:@T.J.Crowder您能否更详细地说明为什么接受的答案不再准确?您链接的问题似乎归结为这样一种观点,即根据规范@zero298:仍然无法保证属性顺序。清楚地描述了自ES2015+起指定的属性顺序。in、Object.keys的旧操作不必正式支持,但现在有了顺序。非正式地说:Firefox、Chrome和Edge都遵循指定的顺序,即使在for和Object.keys中,它们也不是官方要求的:@BenjaminGruenbaum——这正是我的观点。截至2014年,所有主要供应商都有一个共同的实施方案,因此标准最终将在2015年遵循ie。顺便说一句:已经依赖于此@你的评论是错误的。在ES2015中,仅对选定的方法保证订单。请参阅下面的ftor。@mik01aj想链接React的源代码吗?是的,我是lazy@DaveDopson关于第1点。只有非负整数键正确吗?整数键的行为在所有浏览器中都不一致。一些较旧的浏览器使用字符串键按插入顺序迭代整数键,有些则按升序迭代。@DaveDopson-右-过时的浏览器不遵循当前的规范,因为它们没有更新。2020年谁还使用var?
const obj = {};
obj.prop1 = "Foo";
obj.prop2 = "Bar";
obj['1'] = "day";
console.log(obj)

**OUTPUT: {1: "day", prop1: "Foo", prop2: "Bar"}**
    const myMap = new Map()
    // setting the values
    myMap.set("foo", "value associated with 'a string'")
    myMap.set("Bar", 'value associated with keyObj')
    myMap.set("1", 'value associated with keyFunc')

OUTPUT:
**1.    ▶0: Array[2]
1.   0: "foo"
2.   1: "value associated with 'a string'"
2.  ▶1: Array[2]
1.   0: "Bar"
2.   1: "value associated with keyObj"
3.  ▶2: Array[2]
1.   0: "1"
2.   1: "value associated with keyFunc"**