JavaScript 保证对象属性顺序吗?
如果我创建一个像这样的对象:
var obj = {};
obj.prop1 = "Foo";
obj.prop2 = "Bar";
生成的对象总是看起来像这样吗?
{ prop1 : "Foo", prop2 : "Bar" }
也就是说,这些属性的顺序是否与我添加它们的顺序相同?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(16)
自 ES2015 以来,对象的迭代顺序遵循一组特定规则,但它并不(始终)遵循插入顺序。简而言之,迭代顺序是字符串键的插入顺序和类似数字的键的升序的组合:
使用数组或
Map
对象 可能是实现此目的的更好方法。Map
与Object
和 保证按插入顺序迭代键,无一例外:请注意,对象中的属性顺序不能保证全部在 ES2015 之前。对象的定义来自 ECMAScript 第三版 (pdf):
The iteration order for objects follows a certain set of rules since ES2015, but it does not (always) follow the insertion order. Simply put, the iteration order is a combination of the insertion order for strings keys, and ascending order for number-like keys:
Using an array or a
Map
object can be a better way to achieve this.Map
shares some similarities withObject
and guarantees the keys to be iterated in order of insertion, without exception:As a note, properties order in objects weren’t guaranteed at all before ES2015. Definition of an Object from ECMAScript Third Edition (pdf):
是(但并不总是插入顺序)。
大多数浏览器将对象属性迭代为:
一些旧的浏览器组合类别#1和#2,按插入顺序迭代所有键。如果您的键可能解析为整数,则最好不要依赖任何特定的迭代顺序。
当前语言规范(自 ES2015 起) 插入顺序将被保留,但解析为正整数(例如“7”或“99”)的键除外,这种情况下浏览器的行为会有所不同。例如,当键解析为数字时,Chrome/V8 不考虑插入顺序。
旧语言规范(ES2015 之前):迭代顺序在技术上未定义,但所有主要浏览器都遵守 ES2015 行为。
请注意,ES2015 行为是语言规范由现有行为驱动的一个很好的例子,而不是相反。要更深入地了解向后兼容的心态,请参阅 http:// code.google.com/p/v8/issues/detail?id=164,一个 Chrome 错误,详细介绍了 Chrome 迭代顺序行为背后的设计决策。
根据对该错误报告的评论之一(相当固执己见):
YES (but not always insertion order).
Most Browsers iterate object properties as:
Some older browsers combine categories #1 and #2, iterating all keys in insertion order. If your keys might parse as integers, it's best not to rely on any specific iteration order.
Current Language Spec (since ES2015) insertion order is preserved, except in the case of keys that parse as positive integers (eg "7" or "99"), where behavior varies between browsers. For example, Chrome/V8 does not respect insertion order when the keys are parse as numeric.
Old Language Spec (before ES2015): Iteration order was technically undefined, but all major browsers complied with the ES2015 behavior.
Note that the ES2015 behavior was a good example of the language spec being driven by existing behavior, and not the other way round. To get a deeper sense of that backwards-compatibility mindset, see http://code.google.com/p/v8/issues/detail?id=164, a Chrome bug that covers in detail the design decisions behind Chrome's iteration order behavior.
Per one of the (rather opinionated) comments on that bug report:
普通对象中的属性顺序是 JavaScript 中的一个复杂主题。
虽然在 ES5 中明确没有指定顺序,但 ES2015 在某些情况下定义了顺序,并且此后对规范的连续更改越来越多地定义了顺序(甚至,从 ES2020 开始,
for-in
循环的顺序)。给出以下对象:这会产生以下顺序(在某些情况下):
“own”的顺序(非继承)属性 为:
因此,存在三个段,它们可能会改变插入顺序(如示例中所示)。并且类似正整数的键根本不遵守插入顺序。
在 ES2015 中,只有某些方法遵循以下顺序:
从 ES2020 开始,所有其他方法都这样做(有些在 ES2015 和 ES2020 之间的规范中,其他在 ES2020 中) ),其中包括:
最难确定的是
for-in
,因为它独特地包含继承的属性。 已在 ES2020 中完成(除边缘情况外)。链接(现已完成)提案中的以下列表提供了未指定顺序的边缘情况:结论:即使在 ES2015 中,您也不应该依赖 JavaScript 中普通对象的属性顺序。它很容易出错。如果您需要有序命名对,请使用
Map<相反, /code>
纯粹使用插入顺序。如果您只需要顺序,请使用数组或
设置
(也使用纯粹的插入顺序)。Property order in normal Objects is a complex subject in JavaScript.
While in ES5 explicitly no order has been specified, ES2015 defined an order in certain cases, and successive changes to the specification since have increasingly defined the order (even, as of ES2020, the
for-in
loop's order). Given is the following object:This results in the following order (in certain cases):
The order for "own" (non-inherited) properties is:
Thus, there are three segments, which may alter the insertion order (as happened in the example). And positive integer-like keys don't stick to the insertion order at all.
In ES2015, only certain methods followed the order:
As of ES2020, all others do (some in specs between ES2015 and ES2020, others in ES2020), which includes:
The most difficult to nail down was
for-in
because, uniquely, it includes inherited properties. That was done (in all but edge cases) in ES2020. The following list from the linked (now completed) proposal provides the edge cases where the order is not specified:Conclusion: Even in ES2015 you shouldn't rely on the property order of normal objects in JavaScript. It is prone to errors. If you need ordered named pairs, use
Map
instead, which purely uses insertion order. If you just need order, use an array orSet
(which also uses purely insertion order).在撰写本文时,大多数浏览器确实按照插入的顺序返回属性,但明确不能保证其行为,因此不应依赖它。
ECMAScript 规范过去常说:
但是在 ES2015 及更高版本中,非整数键将按插入顺序返回。
At the time of writing, most browsers did return properties in the same order as they were inserted, but it was explicitly not guaranteed behaviour so shouldn't have been relied upon.
The ECMAScript specification used to say:
However in ES2015 and later non-integer keys will be returned in insertion order.
整个答案是在规范合规性的背景下进行的,而不是任何引擎在特定时刻或历史上所做的事情。
一般来说,没有
实际的问题是非常模糊的。
在什么上下文中?
答案是:这取决于许多因素。一般来说,不。
有时,是的
,在这里您可以依靠普通
对象
的属性键顺序:Object.getOwnPropertyNames()
,Reflect.ownKeys()
,Object.getOwnPropertySymbols(O)
在所有情况下,这些方法都包含由
[[OwnPropertyKeys]]
指定的不可枚举属性键和顺序键(见下文) )。它们的不同之处在于所包含的键值类型(String
和/或Symbol
)。在此上下文中,String
包括整数值。Object.getOwnPropertyNames(O)
< /a>返回
O
自己的String
键控属性(属性名称)。Reflect.ownKeys(O)
< /a>返回
O
自己的String
和Symbol
键控属性。Object.getOwnPropertySymbols(O)
< /a>返回
O
自己的Symbol
键控属性。[[OwnPropertyKeys]]
顺序本质上是:类整数
String
按升序排列,非类整数String
按创建顺序排列, 按创建顺序排列的符号。根据调用此函数的函数,可能不包含其中某些类型。具体语言是,key按照以下顺序返回:
Map
如果您对有序地图感兴趣,您应该考虑使用 ES2015 中引入的
Map
类型,而不是普通的Objects
。This whole answer is in the context of spec compliance, not what any engine does at a particular moment or historically.
Generally, no
The actual question is very vague.
In what context?
The answer is: it depends on a number of factors. In general, no.
Sometimes, yes
Here is where you can count on property key order for plain
Objects
:Object.getOwnPropertyNames()
,Reflect.ownKeys()
,Object.getOwnPropertySymbols(O)
In all cases these methods include non-enumerable property keys and order keys as specified by
[[OwnPropertyKeys]]
(see below). They differ in the type of key values they include (String
and / orSymbol
). In this contextString
includes integer values.Object.getOwnPropertyNames(O)
Returns
O
's ownString
-keyed properties (property names).Reflect.ownKeys(O)
Returns
O
's ownString
- andSymbol
-keyed properties.Object.getOwnPropertySymbols(O)
Returns
O
's ownSymbol
-keyed properties.[[OwnPropertyKeys]]
The order is essentially: integer-like
Strings
in ascending order, non-integer-likeStrings
in creation order, Symbols in creation order. Depending which function invokes this, some of these types may not be included.The specific language is that keys are returned in the following order:
Map
If you're interested in ordered maps you should consider using the
Map
type introduced in ES2015 instead of plainObjects
.从 ES2015 开始,某些迭代属性的方法可以保证属性顺序。 但不是其他。不幸的是,不保证顺序的方法通常是最常用的:
Object.keys
、Object.values
、Object.entries
>for..in
循环JSON.stringify
但是,从 ES2020 开始,这些以前不可信方法的属性顺序将由由于 将以与其他规范相同的确定性方式进行迭代rel="noreferrer">完成提案:for-in 机制 。
就像具有保证迭代顺序的方法(例如 Reflect.ownKeys 和 Object.getOwnPropertyNames )一样,之前未指定的方法也将按以下顺序迭代
这几乎是每个实现都已经做的事情(并且已经做了很多年),但新提案已将其正式化。
尽管当前规范留下了 for..in 迭代顺序“几乎完全未指定,真实的引擎往往更加一致:”
因为每个实现都已经可预测地迭代属性,所以可以将其放入规范中而不会破坏向后兼容性。
有一些奇怪的情况,目前的实现不同意,在这种情况下,结果顺序将继续未指定。为了保证属性顺序有保证:
As of ES2015, property order is guaranteed for certain methods that iterate over properties. but not others. Unfortunately, the methods which are not guaranteed to have an order are generally the most often used:
Object.keys
,Object.values
,Object.entries
for..in
loopsJSON.stringify
But, as of ES2020, property order for these previously untrustworthy methods will be guaranteed by the specification to be iterated over in the same deterministic manner as the others, due to to the finished proposal: for-in mechanics.
Just like with the methods which have a guaranteed iteration order (like
Reflect.ownKeys
andObject.getOwnPropertyNames
), the previously-unspecified methods will also iterate in the following order:This is what pretty much every implementation does already (and has done for many years), but the new proposal has made it official.
Although the current specification leaves for..in iteration order "almost totally unspecified, real engines tend to be more consistent:"
Because every implementation already iterates over properties predictably, it can be put into the specification without breaking backwards compatibility.
There are a few weird cases which implementations currently do not agree on, and in such cases, the resulting order will continue be unspecified. For property order to be guaranteed:
在现代浏览器中,您可以使用
Map
数据结构而不是对象。开发者 mozilla >地图
In modern browsers you can use the
Map
data structure instead of a object.Developer mozilla > Map
在 ES2015 中,确实如此,但并不像您想象的那样
。直到 ES2015 才保证对象中键的顺序。它是由实现定义的。
然而,在 ES2015 中它被指定了。与 JavaScript 中的许多事情一样,这样做是出于兼容性目的,并且通常反映了大多数 JS 引擎中现有的非官方标准(you-know-who 是一个例外)。
该顺序在规范中的抽象操作 下定义OrdinaryOwnPropertyKeys,它支持迭代对象自己的键的所有方法。解释一下,顺序如下:
所有整数索引键(例如
“1123”
,“55”
等)数字升序。所有不是整数索引的字符串键,按创建顺序(最旧的在前)。
所有符号键,按创建顺序(最旧的在前)。
说这个顺序不可靠是愚蠢的——它是可靠的,只是可能不是你想要的,而现代浏览器正确地实现了这个顺序。
一些例外情况包括枚举继承键的方法,例如
for .. in
循环,它不保证按照规范的顺序。In ES2015, it does, but not to what you might think
The order of keys in an object wasn't guaranteed until ES2015. It was implementation-defined.
However, in ES2015 it was specified. Like many things in JavaScript, this was done for compatibility purposes and generally reflected an existing unofficial standard among most JS engines (with you-know-who being an exception).
The order is defined in the spec, under the abstract operation OrdinaryOwnPropertyKeys, which underpins all methods of iterating over an object's own keys. Paraphrased, the order is as follows:
All integer index keys (stuff like
"1123"
,"55"
, etc) in ascending numeric order.All string keys which are not integer indices, in order of creation (oldest-first).
All symbol keys, in order of creation (oldest-first).
It's silly to say that the order is unreliable - it is reliable, it's just probably not what you want, and modern browsers implement this order correctly.
Some exceptions include methods of enumerating inherited keys, such as the
for .. in
loop, that doesn't guarantee order according to the specification.正如其他人所说,当您迭代对象的属性时,您无法保证顺序。如果您需要多个字段的有序列表,我建议创建一个对象数组。
这样您就可以使用常规的 for 循环并获得插入顺序。如果需要,您可以使用数组排序方法将其排序到新数组中。
As others have stated, you have no guarantee as to the order when you iterate over the properties of an object. If you need an ordered list of multiple fields I suggested creating an array of objects.
This way you can use a regular for loop and have the insert order. You could then use the Array sort method to sort this into a new array if needed.
对象和 MAP 之间的主要区别(以示例为例):
它是循环中的迭代顺序,在 Map 中它遵循创建时设置的顺序,而在 OBJECT 中则不然。
看:
对象
地图
Major Difference between Object and MAP with Example :
it's Order of iteration in loop, In Map it follows the order as it was set while creation whereas in OBJECT does not.
SEE:
OBJECT
MAP
刚刚发现这一点很困难。
将 React 与 Redux 结合使用,每次存储更改时,我想要遍历其键以生成子项的状态容器都会刷新(根据 Redux 的不变性概念)。
因此,为了获取
Object.keys(valueFromStore)
,我使用了Object.keys(valueFromStore).sort()
,这样我至少现在有了按字母顺序排列的钥匙。Just found this out the hard way.
Using React with Redux, the state container of which's keys I want to traverse in order to generate children is refreshed everytime the store is changed (as per Redux's immutability concepts).
Thus, in order to take
Object.keys(valueFromStore)
I usedObject.keys(valueFromStore).sort()
, so that I at least now have an alphabetical order for the keys.对于 100% 故障安全的解决方案,您可以使用嵌套对象并执行如下操作:
For a 100% fail-safe solution you could use nested objects and do something like this:
实际上,它并不能保证顺序,但最有可能的是,它通过插入项目来排序,但在某些情况下顺序很容易发生变化。例如,如果您使用 Object.键和对象。值,它们的结果在第一顺序上并不相同。 保存顺序的可能
解决方案是保存键并使用这些键以第一顺序访问对象。
像这样的东西:
Actually, it doesn't guarantee order, but most likely, it orders by inserting items, but in some cases the order is change prone .For instance, if you use Object. Keys and Object. Values, the results of them aren't the same in first order. Likely
A solution to save order is to save keys and use those keys to access the object in the first order.
Something like this:
实际的键顺序可能与插入顺序不同:
结果是:
所以不要依赖插入顺序。
The actual key order can be different from the insertion order:
And the result is:
So don't rely on the insertion order.
编辑:下面的帖子只是开发者工具显示问题。正如@Bergi 所指出的,迭代顺序如其他答案中所述。我把它留在这里以防其他人发现同样的陷阱。作为记录,我的代码使用 Vite 进行编译,
"react": "^18.3.1"
,并且我在 chrome 和 Firefox 中对此进行了测试。我的一些依赖属性顺序的代码刚刚崩溃了。今天我做了测试,发现它们现在的顺序如下:
因此,在我插入排序后的属性后,它们会恢复到这个顺序。这适用于
Object.fromEntries
和带括号的插入。以下代码:打印此内容:
Edit: the below post was only a developer tools display issue. As pointed out by @Bergi, the iteration order is as described in other answers. I leave it here in case others find the same pitfall. For the records, my code compiles with Vite,
"react": "^18.3.1"
, and I tested this in both chrome and Firefox.Some of my code relying on the order of properties just broke. Today I made tests and found that they are now ordered like this:
Hence, after I insert my sorted properties, they are put back to this order. This holds with
Object.fromEntries
and with insertion with brackets. The following code:Prints this:
来自 JSON 标准:
(强调我的)。
所以,不,你不能保证订单。
From the JSON standard:
(emphasis mine).
So, no you can't guarantee the order.