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

时间:2011-04-02 20:54:12

标签: javascript object

如果我创建这样的对象:

var obj = {};
obj.prop1 = "Foo";
obj.prop2 = "Bar";

生成的对象总是会是这样的吗?

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

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

12 个答案:

答案 0 :(得分:400)

不,JavaScript中不保证对象中的属性顺序;你需要使用Array

来自ECMAScript Third Edition (pdf)的对象的定义:

  

4.3.3对象

     

一个对象是一个成员   类型对象。 这是一个无序的属性集合   包含原始值,对象或   功能。存储在a中的函数   对象的属性称为a   方法

自ECMAScript 2015 以来,使用the Map object可能是另一种选择。 MapObjectguarantees the keys order分享一些相似之处:

  

Map以插入顺序迭代其元素,而没有为Objects指定迭代顺序。

答案 1 :(得分:148)

当前语言规范(自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       将事实上的标准行为纳入规范的下一轮。

答案 2 :(得分:66)

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

ECMAScript specification曾经说过:

  

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

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

答案 3 :(得分:66)

普通对象中的属性顺序是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},
});

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

Object {
  0: 0,
  1: "1",
  2: "2",
  b: "b",
  a: "a",
  m: function() {},
  Symbol(): "sym"
}
  1. 按升序排列的类似整数的键
  2. 插入顺序中的普通键
  3. 按插入顺序排列的符号
  4. 因此,有三个段可能会改变插入顺序(如示例中所示)。类似整数的键根本不会影响插入顺序。

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

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

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

    以下方法/循环保证完全没有订单:

    • Object.keys
    • 的for..in
    • JS​​ON.parse
    • JS​​ON.stringify

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

答案 4 :(得分: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.   
  3. ... P的每个属性键O,它是一个字符串但不是整数索引,属性创建顺序

  4.   
  5. ...属性创建顺序为P的{​​{1}}的每个属性键O

  6.   

Map

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

答案 5 :(得分:12)

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

Developer mozilla > Map

  

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

答案 6 :(得分:5)

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

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

这样您就可以使用常规for循环并具有插入顺序。然后,您可以根据需要使用Array排序方法将其排序为新数组。

答案 7 :(得分:4)

在ES2015中,它可以,但是不符合您的想法

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

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

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

  1. 所有整数索引键(诸如"1123""55"之类的东西)都按数字升序排列。

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

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

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

某些异常包括枚举继承键的方法,例如for .. in循环。 for .. in循环不保证根据规范的顺序。

答案 8 :(得分:2)

只是很难找到答案。

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

因此,为了使用Object.keys(valueFromStore),我使用了Object.keys(valueFromStore).sort(),以便至少现在对这些键按字母顺序排序。

答案 9 :(得分:1)

从ES2015开始,某些迭代属性的方法保证了属性顺序。 but not others。不幸的是,不能保证有顺序的方法通常是最常用的:

  • Object.keysObject.valuesObject.entries
  • for..in循环
  • JSON.stringify

但是很快(可能在ES2020中),规范将保证这些以前不可信任的方法 的属性顺序以与确定方法相同的确定性方式进行迭代。其他由于stage 4提案:For-in enumeration order

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

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

这几乎是每个实现都要做的,但是新提案将使其正式化。

尽管当前规范以。almost totally unspecified的迭代顺序保留,但实际引擎趋向于更加一致:”

  

ECMA-262中缺乏特异性并不能反映现实。在过去的讨论中,实现者观察到for-for的行为存在一些约束,任何想要在网络上运行代码的人都必须遵循。

由于每个实现都已经可以预测地遍历属性,因此可以将其放入规范中而不会破坏向后兼容性。


有几种奇怪的情况,当前实现 不能达成共识,在这种情况下,结果顺序将继续不确定。对于财产订单to be guaranteed

  

被迭代的对象或其原​​型链中的任何内容都不是代理,类型化数组,模块名称空间对象或宿主奇异对象。

     

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

     

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

     

对象的原型链中没有任何内容在迭代过程中添加。

     

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

     

没有不可枚举的属性会遮盖可枚举的属性。

答案 10 :(得分:0)

对象与MAP之间的主要差异,例如:

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

查看: 对象

var obj = {};
obj.prop1 = "Foo";
obj.prop2 = "Bar";
obj['1'] = "day";
console.log(obj)

**OUTPUT: {1: "day", prop1: "Foo", prop2: "Bar"}**

地图

    let 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"**

答案 11 :(得分:-5)

来自JSON standard

  

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

(强调我的)。

所以,不,你不能保证订单。