“for (... in ...)”循环中的元素顺序

IT技术 javascript for-loop
2021-02-07 02:19:32

Javascript 中的“for...in”循环是否按照声明的顺序遍历哈希表/元素?是否有浏览器不按顺序执行?
我希望使用的对象将被声明一次并且永远不会被修改。

假设我有:

var myObject = { A: "Hello", B: "World" };

我进一步将它们用于:

for (var item in myObject) alert(item + " : " + myObject[item]);

在大多数体面的浏览器中,我可以期望 'A : "Hello"' 总是出现在 'B : "World"' 之前吗?

6个回答

引用 John Resig 的话

目前,所有主要浏览器都按照定义的顺序循环遍历对象的属性。除了少数情况外,Chrome 也会这样做。[...] ECMAScript 规范明确未定义此行为。在 ECMA-262 中,第 12.6.4 节:

枚举属性的机制......取决于实现。

但是,规范与实现完全不同。ECMAScript 的所有现代实现都按照定义的顺序遍历对象属性。因此,Chrome 团队认为这是一个错误,并将对其进行修复。

除了 Chrome和 Opera之外,所有浏览器都遵守定义顺序它们对每个非数字属性名称都有处理。在这两个浏览器中,属性在第一个非数字属性之前按顺序拉取(这与它们如何实现数组有关)。顺序也是一样的Object.keys

这个例子应该清楚地说明会发生什么:

var obj = {
  "first":"first",
  "2":"2",
  "34":"34",
  "1":"1",
  "second":"second"
};
for (var i in obj) { console.log(i); };
// Order listed:
// "1"
// "2"
// "34"
// "first"
// "second"

这方面的技术细节不如这可能随时改变的事实重要。不要依赖这样的事情。

简而言之:如果顺序对您很重要,请使用数组。

@HM2K,同样的事情,规范说“对象是零个或多个名称/值对的无序集合。” JSON 不是 JavaScript:服务器不需要(并且可能不会)尊重您处理它的顺序。
2021-03-10 02:19:32
Firefox 自 21 版本以来似乎不再尊重插入顺序。
2021-03-17 02:19:32
这个答案在 ES2015 中是错误的。
2021-03-22 02:19:32
并不真地。Chrome 没有实现与其他浏览器相同的顺序:code.google.com/p/v8/issues/detail?id=164
2021-04-05 02:19:32
“如果顺序对您很重要,请使用数组”:当您使用 JSON 时呢?
2021-04-08 02:19:32

一年后撞上这个...

现在是2012 年,主要浏览器仍然不同:

function lineate(obj){
    var arr = [], i;
    for (i in obj) arr.push([i,obj[i]].join(':'));
    console.log(arr);
}
var obj = { a:1, b:2, c:3, "123":'xyz' };
/* log1 */  lineate(obj);
obj.a = 4;
/* log2 */  lineate(obj);
delete obj.a;
obj.a = 4;
/* log3 */  lineate(obj);

在当前浏览器中进行要点测试

Safari 5、火狐 14

["a:1", "b:2", "c:3", "123:xyz"]
["a:4", "b:2", "c:3", "123:xyz"]
["b:2", "c:3", "123:xyz", "a:4"]

Chrome 21、Opera 12、Node 0.6、Firefox 27

["123:xyz", "a:1", "b:2", "c:3"]
["123:xyz", "a:4", "b:2", "c:3"]
["123:xyz", "b:2", "c:3", "a:4"]

IE9

[123:xyz,a:1,b:2,c:3] 
[123:xyz,a:4,b:2,c:3] 
[123:xyz,a:4,b:2,c:3] 
nickf:实现是正确的,因为它们按照规范进行操作。我想每个人都同意 javascript 规范是......好吧,我不想使用“错误”这个词,“非常愚蠢和烦人”怎么样?:P
2021-03-12 02:19:32
在最先进的实现中,由于由实数组支持,您将按数值顺序接收整数属性,并且由于隐藏类/形状公式,将按插入顺序接收命名属性。不同之处在于它们是首先列出整数属性还是首先列出命名属性。的添加delete很有趣,因为至少在 V8 中,它立即导致对象由哈希表备份。但是,V8 中的哈希表是按插入顺序存储的。这里最有趣的结果是 IE,我想知道他们做了什么样的丑陋来实现它......
2021-03-12 02:19:32
@boxed:将对象视为哈希映射(表/任何东西)。在大多数语言(Java、Python 等)中,这些类型的数据结构没有排序。所以这在 JavaScript 中也是同样的情况并不奇怪,它当然不会使规范错误或愚蠢。
2021-03-18 02:19:32
这里有什么问题?正如规范所说,它是一个键/值对的无序列表。
2021-03-20 02:19:32
老实说,我不明白这个答案的意义(抱歉)。属性迭代的顺序是一个实现细节,浏览器使用不同的 JavaScript 引擎,所以顺序应该不同。这不会改变。
2021-04-02 02:19:32

来自ECMAScript Language Specification,第 12.6.4 节(关于for .. in循环):

枚举属性的机制取决于实现。枚举的顺序由对象定义。

以及第 4.3.3 节(“对象”的定义):

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

我想这意味着你不能依赖于在 JavaScript 实现中以一致的顺序枚举的属性。(无论如何,依赖一种语言的特定于实现的细节都是不好的风格。)

如果你想定义你的顺序,你需要实现一些定义它的东西,比如在访问对象之前排序的键数组。

for/in 枚举的对象元素是没有设置 DontEnum 标志的属性。ECMAScript,又名 Javascript,标准明确指出“一个对象是一个无序的属性集合”(参见http://www.mozilla.org/js/language/E262-3.pdf第 8.6 节)。

假设所有 Javascript 实现都将按声明顺序枚举,这将不符合标准(即安全)。

这就是为什么他要问这个问题,而不仅仅是假设:p
2021-03-16 02:19:32

迭代顺序也与删除属性混淆,但在这种情况下仅适用于 IE。

var obj = {};
obj.a = 'a';
obj.b = 'b';
obj.c = 'c';

// IE allows the value to be deleted...
delete obj.b;

// ...but remembers the old position if it is added back later
obj.b = 'bb';
for (var p in obj) {
    alert(obj[p]); // in IE, will be a, bb, then c;
                   // not a, c, then bb as for FF/Chrome/Opera/Safari
}

如果http://code.google.com/p/v8/issues/detail?id=164的讨论有任何迹象,那么更改规范以修复迭代顺序的愿望似乎是开发人员的一个普遍愿望