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

Posted

技术标签:

【中文标题】“for (... in ...)”循环中的元素顺序【英文标题】:Elements order in a "for (… in …)" loop 【发布时间】:2010-09-21 19:07:24 【问题描述】:

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

假设我有:

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

我进一步使用它们:

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

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

【问题讨论】:

因为他们只会测试潜在浏览器和变体的子集。更不用说任何未来的浏览器了。假设一个不失败的测试提供任何具体的证据是完全错误的。 我怀疑我自己有限的 javascript 能力会比 SO 人群更好。除了谁知道那里潜伏着什么奇怪的浏览器吗?您可以在答案中看到 GChrome 确实有一个错误,这在我的简单示例中并不明显。 Does JavaScript Guarantee Object Property Order? 的可能重复项 也相关:Does ES6 introduce a well-defined order of enumeration for object properties? 这能回答你的问题吗? Does ES6 introduce a well-defined order of enumeration for object properties? 【参考方案1】:

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

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

【讨论】:

这就是他问这个问题的原因,而不是仅仅假设:p【参考方案2】:

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

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

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

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

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

如果您想定义您的订单,您将需要实现一些定义它的东西,例如在使用它访问对象之前排序的键数组。

【讨论】:

【参考方案3】:

Quoting John Resig:

目前所有主流浏览器都按顺序循环遍历对象的属性 他们被定义了。 Chrome 也这样做,除了几个案例。 [...] ECMAScript 规范明确未定义此行为。 在 ECMA-262 中,第 12.6.4 节:

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

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

所有浏览器都尊重定义顺序 with the exception of 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"

这方面的技术细节不如它随时可能改变的事实重要。不要指望事情会保持这种状态。

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

【讨论】:

并非如此。 Chrome 没有实现与其他浏览器相同的顺序:code.google.com/p/v8/issues/detail?id=164 “如果顺序对您很重要,请使用数组”:当您使用 JSON 时会怎样? @HM2K,同样的事情,规范说“一个对象是零个或多个名称/值对的无序集合。” JSON 不是 JavaScript:服务器不需要(也可能不会)尊重您处理它的顺序。 Firefox,因为它的 21 版本似乎不再尊重插入顺序。 这个答案在 ES2015 中是错误的。【参考方案4】:

在 IE6 中,不保证顺序。

【讨论】:

嗯,我的意思是...,它是 IE6。【参考方案5】:

订单不可信。 Opera 和 Chrome 都返回无序的属性列表。

<script type="text/javascript">
var username = "14719":"A","648":"B","15185":"C";

for (var i in username) 
  window.alert(i + ' => ' + username[i]);

</script>

上面的代码在 Opera 中显示了 B、A、C,在 Chrome 中显示了 C、A、B。

【讨论】:

【参考方案6】:

在删除属性方面,迭代顺序也很混乱,但在这种情况下仅限于 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 的讨论有任何迹象,那么更改规范以修复迭代顺序的愿望似乎在开发人员中很受欢迎。

【讨论】:

【参考方案7】:

一年后再碰这个……

现在是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);

gist 或 test in current browser

Safari 5、Firefox 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 @boxed:将对象视为哈希映射(表/其他)。在大多数语言(Java、Python 等)中,这些类型的数据结构都没有排序。因此,这在 JavaScript 中也是如此也就不足为奇了,它当然不会使规范错误或愚蠢。 老实说,我不明白这个答案的意义(对不起)。属性迭代的顺序是一个实现细节,并且浏览器使用不同的 JavaScript 引擎,因此预计顺序会有所不同。这不会改变。 在最先进的实现中,由于由真实数组支持,您将按数值顺序接收整数属性,并且由于隐藏的类/形状公式,将按插入顺序接收命名属性。可能会有所不同的是,它们是先列出整数属性还是先列出命名属性。 delete 的添加很有趣,因为至少在 V8 中,它会立即导致对象被哈希表备份。但是,V8 中的哈希表以插入顺序存储。这里最有趣的结果是 IE,我想知道他们做了什么样的丑陋来实现它......【参考方案8】:

这本身并没有回答问题,而是为基本问题提供了解决方案。

假设您不能依赖顺序来保存,为什么不使用具有键和值作为属性的对象数组?

var myArray = [
    
        'key'   : 'key1'
        'value' : 0
    ,
    
        'key'   : 'key2',
        'value' : 1
     // ...
];

现在,由您来确保密钥是唯一的(假设这对您也很重要)。同样,直接寻址也发生了变化,for (...in...) 现在将索引返回为“键”。

> console.log(myArray[0].key);
key1

> for (let index in myArray) console.log(myArray[index].value);
0
1
请参阅CodePen 上 JDQ (@JDQ) 的 Pen for (...in...) addressing in order。

【讨论】:

【参考方案9】:

正如其他答案所述,不,不保证顺序。

如果你想按顺序迭代,你可以这样做:

let keys = Object.keys(myObject);
for (let key of keys.sort()) 
  let value = myObject[key];
  
  // Do what you want with key and value 

请注意,就性能而言,这并不是最优的,但是当您想要一个漂亮的字母显示时,这就是价格。

【讨论】:

如果有人正在寻找以确定的顺序对对象的属性进行排序的实现,那么这是正确的答案。【参考方案10】:

截至 2022 年

我刚刚发现(令我惊讶)Chrome 不遵守定义顺序。它宁可按字母顺序排序,但不是自然。例如键“a12”出现在“a3”之前。所以要小心。

【讨论】:

以上是关于“for (... in ...)”循环中的元素顺序的主要内容,如果未能解决你的问题,请参考以下文章

虽然不能保证 for..in 循环中的元素顺序,但在实践中实现之间存在啥偏差? [关闭]

如何在 Jinja2 模板 Django 中计算循环“for in”中的所有元素

JS中for...in 语句用于对数组或者对象的属性进行循环操作吗?

JavaScript中的for in 循环

for...in和for...of循环的区别

for循环,foreach, map,reduce用法对比+for in,for of