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