元素在“ for(…in…)”循环中排序


205

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

假设我有:

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

我进一步将它们用于:

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

我是否可以期望在大多数体面的浏览器中,“ A:“ Hello””总是排在“ B:“ World””之前?


61
因为他们只会测试潜在浏览器和变体的一个子集。更不用说任何将来的浏览器了。假设一个不失败的测试提供了任何形式的具体证据,这是错误的。
詹姆斯·休斯

6
我怀疑我自己有限的javascript能力会比SO人群更好。除了谁知道什么奇怪的浏览器潜伏在那里?您会在答案中看到GChrome确实存在一个错误,在我的简单示例案例中该错误将不明显。
chakrit


Answers:


214

引用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"

它的技术性不如可能随时更改的事实重要。不要依赖保持这种状态的事物。

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


3
并不是的。Chrome的执行顺序与其他浏览器不同:code.google.com/p/v8/issues/detail?id=164
Tim Down

19
“如果顺序对您很重要,请使用数组”:使用JSON时如何?
HM2K'2

11
@ HM2K,同一件事,规范说:“对象是零个或多个名称/值对的无序集合。” JSON不是JavaScript:服务器不需要(也可能不会)遵守您的处理顺序。
Borgar

7
从21版本开始,Firefox似乎不再遵守插入顺序。
Doc Davluz 2013年

2
这个答案在ES2015中是错误的。
本杰明·格林巴姆

54

一年后撞到这个...

现在是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,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] 

16
这是什么问题?恰如规范所说,它是键/值对的无序列表。
尼克

5
nickf:实现是正确的,因为它们按照规范的要求进行操作。我想每个人都同意javascript规范是..好吧,我不想使用“错误”一词,“极端愚蠢和烦人”又如何呢?:P
盒装

10
@boxed:将对象视为哈希表(表/其他)。在大多数语言(Java,Python等)中,这些类型的数据结构均未排序。因此,在JavaScript中也是如此也是不足为奇的,并且它当然不会使规范错误或愚蠢。
Felix Kling 2013年

9
老实说,我看不出这个答案的意思(对不起)。属性的迭代顺序是实现细节,并且浏览器使用不同的JavaScript引擎,因此可以预期顺序会有所不同。这不会改变。
Felix Kling 2013年

2
在最先进的实现中,由于有实数组支持,您将按数值顺序接收整数属性,由于隐藏的类/形状公式,将按插入顺序接收命名属性。可以变化的是它们是首先列出整数属性还是首先列出命名属性。添加的delete是有趣的,因为至少在V8中,它立即使对象由哈希表进行备份。但是,V8中的哈希表按插入顺序存储。此处最有趣的结果是IE,我想知道他们做了什么样的丑陋工作……
Esailija

27

ECMAScript语言规范的 12.6.4节(for .. in循环中):

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

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

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

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

如果要定义订单,则需要实现一些定义它的东西,例如在访问带有该对象的对象之前对键进行排序的数组。


10

用于枚举的对象的元素是未设置DontEnum标志的属性。ECMAScript(又名Javascript)标准明确表示“对象是属性的无序集合”(请参见http://www.mozilla.org/js/language/E262-3.pdf第8.6节)。

假定所有Javascript实现将按声明顺序枚举并不是符合标准(即安全)的。


2
这就是为什么他问这个问题,而不是仅仅假设:p
Jamie Pate

4

关于删除属性,迭代顺序也很混乱,但是在这种情况下,仅使用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上的讨论有任何迹象,那么更改规范以固定迭代顺序的愿望似乎在开发人员中非常普遍。


3

在IE6中,无法保证顺序。


2

该订单不能被信任。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。


1

这本身并不能回答问题,但是可以为基本问题提供解决方案。

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

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
有关CodePenJDQ@JDQ)的顺序,请参见笔中的(... in ...)寻址

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.