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

2024-11-02 21:00:00
admin
原创
41
摘要:问题描述:如果我创建一个这样的对象:var obj = {}; obj.prop1 = "Foo"; obj.prop2 = "Bar"; 最终的物体看起来总是这个样子吗?{ prop1 : "Foo"...

问题描述:

如果我创建一个这样的对象:

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

最终的物体看起来总是这个样子吗?

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

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


解决方案 1:

自 ES2015 以来,对象的迭代顺序遵循一组特定的规则,但它并不总是遵循插入顺序。简而言之,迭代顺序是字符串键的插入顺序和数字键的升序的组合:

// key order: 1, foo, bar
const obj = { "foo": "foo", "1": "1", "bar": "bar" }

使用数组或Map对象可能是实现此目的的更好方法。Map与 有相似之处Object并保证按插入顺序迭代键,无一例外:

Map 中的键是有序的,而添加到对象的键则不是。因此,在对其进行迭代时,Map 对象会按插入顺序返回键。(请注意,在 ECMAScript 2015 规范中,对象确实保留了字符串和符号键的创建顺序,因此仅使用字符串键遍历对象会产生按插入顺序排列的键)

需要注意的是,在 ES2015 之前,对象中的属性顺序根本无法得到保证。ECMAScript第三版 (pdf)中对象的定义:

4.3.3 对象

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

解决方案 2:

是的(但不总是插入顺序)。

大多数浏览器以如下方式迭代对象属性:

  1. 按升序排列的正整数键(以及解析为整数的字符串,如“1”)

  2. 字符串键,按插入顺序排列(ES2015 保证这一点,并且所有浏览器都遵守)

  3. 符号名称,按插入顺序排列(ES2015 保证这一点,并且所有浏览器都遵守)

一些较旧的浏览器会将类别 #1 和 #2 组合起来,按插入顺序迭代所有键。如果您的键可能解析为整数,则最好不要依赖任何特定的迭代顺序。

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

解决方案 3:

普通对象中的属性顺序是 JavaScript 中的一个复杂主题。

虽然 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"
}

“自身”(非继承)属性的顺序如下:

  1. 按升序排列的正整数类键

  2. 按插入顺序排列的字符串键

  3. 按插入顺序排列的符号

因此,有三个段可能会改变插入顺序(如示例中所示)。而正整数类键根本不遵循插入顺序。

在 ES2015 中,只有某些方法遵循以下顺序:

  • 对象分配

  • 对象定义属性

  • 对象.getOwnPropertyNames

  • 对象.getOwnPropertySymbols

  • Reflect.ownKeys

  • JSON.解析

  • JSON.stringify

从 ES2020 开始,其他所有规范都支持该规范(一些规范在 ES2015 和 ES2020 之间,其他规范在 ES2020 中),其中包括:

  • 对象.keys、对象.entries、对象.values、...

  • 对于...在

最难确定的是for-in,它独特地包含了继承的属性。ES2020中已经实现了这一点(除了极端情况外)。以下来自链接(现已完成)提案的列表提供了未指定顺序的极端情况:

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

  • 在迭代过程中,对象及其原型链中的任何内容的原型都不会发生变化。

  • 该对象或其原​​型链中的任何内容均不具有在迭代期间被删除的属性。

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

  • 对象的任何属性或其原型链中的任何内容的可枚举性在迭代期间都不会发生变化。

  • 任何不可枚举的属性都不会掩盖可枚举的属性。

结论:即使在 ES2015 中,您也不应该依赖 JavaScript 中普通对象的属性顺序。这样很容易出错。如果您需要有序的命名对,请使用Map纯粹使用插入顺序的。如果您只需要顺序,请使用数组或Set(也使用纯粹的插入顺序)。

解决方案 4:

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

ECMAScript 规范曾经说过:

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

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

解决方案 5:

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

一般情况下,不

实际问题非常模糊。

这些属性的顺序是否与我添加的顺序相同

在什么情况下?

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

有时是的

您可以在此处依赖 plain 的属性键顺序Objects

  • 符合 ES2015 标准的引擎

  • 拥有房产

  • Object.getOwnPropertyNames(),,Reflect.ownKeys()Object.getOwnPropertySymbols(O)

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

Object.getOwnPropertyNames(O)

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

Reflect.ownKeys(O)

返回O自己的String- 和Symbol-keyed 属性。

Object.getOwnPropertySymbols(O)

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

[[OwnPropertyKeys]]

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

具体语言是按以下顺序返回键:

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

  2. ... 每个属性P的键O都是字符串,但不是整数索引,按属性创建顺序

  3. ... 每个自己的属性键P都是O一个符号,按照属性创建顺序

Map

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

解决方案 6:

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

  • Object.keys,,Object.valuesObject.entries

  • for..in循环

  • JSON.stringify

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

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

  • 数字数组键,按数字升序排列

  • 所有其他非符号键,按插入顺序

  • 符号键,按插入顺序

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

尽管当前规范对迭代顺序的描述“几乎完全没有指定,但实际引擎往往更加一致:”

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

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


目前,实现中还存在一些奇怪的情况,在这种情况下,结果顺序将继续不明确。为了保证属性顺序,请执行以下操作:

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

在迭代过程中,对象及其原型链中的任何内容的原型都不会发生变化。

该对象或其原​​型链中的任何内容均不具有在迭代期间被删除的属性。

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

对象的任何属性或其原型链中的任何内容的可枚举性在迭代期间都不会发生变化。

任何不可枚举的属性都不会掩盖可枚举的属性。

解决方案 7:

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

开发人员 mozilla > 地图

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

解决方案 8:

在 ES2015 中确实如此,但可能和你想象的不一样

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

然而,ES2015 对此进行了详细说明。与 JavaScript 中的许多事情一样,这样做是为了兼容性,并且通常反映了大多数 JS 引擎中现有的非官方标准(你知道的引擎是例外)。

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

  1. 所有整数索引键(诸如"1123""55"等)按数字升序排列。

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

  3. 所有符号键,按创建顺序排列(从旧到新)。

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

一些例外包括枚举继承键的方法,例如循环for .. in,它不能保证按照规范的顺序。

解决方案 9:

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

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

这样,您可以使用常规 for 循环并设置插入顺序。然后,如果需要,您可以使用数组排序方法将其排序到新数组中。

解决方案 10:

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

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

参见:
物体

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

解决方案 11:

刚刚才发现这是很困难的事情。

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

因此,为了采取Object.keys(valueFromStore)我使用的Object.keys(valueFromStore).sort(),以便我现在至少有一个按字母顺序排列的按键。

解决方案 12:

对于 100% 故障安全解决方案,您可以使用嵌套对象并执行如下操作:

const obj = {};
obj.prop1 = {content: "Foo", index: 0};
obj.prop2 = {content: "Bar", index: 1};

for (let i = 0; i < Object.keys(obj).length; i++)
for (const prop in obj) {
    if (obj[prop].index == i) {
        console.log(obj[prop].content);
        break;
    }
}

解决方案 13:

实际上,它并不能保证顺序,但大多数情况下,它通过插入项目来排序,但在某些情况下顺序很容易改变。例如,如果你使用 Object.Keys 和 Object.Values,它们的结果在第一个顺序上是不一样的。很可能

保存顺序的一个解决方案是保存键并使用这些键按照第一个顺序访问对象。

像这样:

MergeFlatedObjValue(obj){
  const myJSONString = JSON.stringify(obj);
  const regexp = /(?<!:)("[w.]+")/g;
let matches = myJSONString.matchAll(regexp);
var MergeFlatedObjValue="";   
for (const match of matches) {
  var matched=match[0];
   matched=matched.replace(/['"]+/g, '');
  MergeFlatedObjValue+=obj[matched]+"#";

}
  return MergeFlatedObjValue;

  }

解决方案 14:

实际的键顺序可能与插入顺序不同:

console.log({ 5: 5, 2: 2 })

结果是:

{ '2': 2, '5': 5 }

所以不要依赖插入顺序。

解决方案 15:

编辑:以下帖子只是开发人员工具显示问题。正如@Bergi指出的那样,迭代顺序与其他答案中描述的一样。我把它留在这里以防其他人发现同样的陷阱。为了记录,我的代码用Vite编译,"react": "^18.3.1"我在chrome和Firefox中都测试了这一点。

我的一些依赖属性顺序的代码刚刚被破坏了。今天我进行了测试,发现它们现在的顺序如下:

  1. 数字

  2. 大写字母顺序

  3. 小写,字母顺序

因此,在我插入已排序的属性后,它们会恢复到此顺序。这适用于Object.fromEntries带括号的插入。以下代码:

let test = {}
test["b"] = "insert order 1"
test["a"] = "insert order 2"
test[1] = "insert order 3"
test["B"] = "insert order 4"
test["A"] = "insert order 5"
test["C"] = "insert order 6"
console.log(test)

打印此内容:

新对象属性排序

解决方案 16:

来自JSON 标准:

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

(重点是我的)。

因此,你不能保证订单。

相关推荐
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   601  
  华为IPD与传统研发模式的8大差异在快速变化的商业环境中,产品研发模式的选择直接决定了企业的市场响应速度和竞争力。华为作为全球领先的通信技术解决方案供应商,其成功在很大程度上得益于对产品研发模式的持续创新。华为引入并深度定制的集成产品开发(IPD)体系,相较于传统的研发模式,展现出了显著的差异和优势。本文将详细探讨华为...
IPD流程是谁发明的   7  
  如何通过IPD流程缩短产品上市时间?在快速变化的市场环境中,产品上市时间成为企业竞争力的关键因素之一。集成产品开发(IPD, Integrated Product Development)作为一种先进的产品研发管理方法,通过其结构化的流程设计和跨部门协作机制,显著缩短了产品上市时间,提高了市场响应速度。本文将深入探讨如...
华为IPD流程   9  
  在项目管理领域,IPD(Integrated Product Development,集成产品开发)流程图是连接创意、设计与市场成功的桥梁。它不仅是一个视觉工具,更是一种战略思维方式的体现,帮助团队高效协同,确保产品按时、按质、按量推向市场。尽管IPD流程图可能初看之下显得错综复杂,但只需掌握几个关键点,你便能轻松驾驭...
IPD开发流程管理   8  
  在项目管理领域,集成产品开发(IPD)流程被视为提升产品上市速度、增强团队协作与创新能力的重要工具。然而,尽管IPD流程拥有诸多优势,其实施过程中仍可能遭遇多种挑战,导致项目失败。本文旨在深入探讨八个常见的IPD流程失败原因,并提出相应的解决方法,以帮助项目管理者规避风险,确保项目成功。缺乏明确的项目目标与战略对齐IP...
IPD流程图   8  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用