222

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

假设我有:

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

我进一步将它们用于:

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

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

4

10 回答 10

229

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

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

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

于 2008-11-11T13:24:20.847 回答
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);

在当前浏览器中进行gist或测试

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、节点 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] 
于 2012-01-02T19:02:21.573 回答
28

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

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

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

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

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

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

于 2008-11-11T12:14:34.357 回答
10

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

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

于 2008-11-11T12:14:15.277 回答
5

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

于 2011-01-04T07:44:04.447 回答
3

在 IE6 中,不保证顺序。

于 2008-12-08T19:49:54.513 回答
3

订单不可信。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。

于 2010-08-25T14:57:34.677 回答
2

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

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

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 )顺序寻址的 (...in...)笔。

于 2018-04-29T22:05:05.617 回答
2

正如其他答案所述,不,不能保证订单。

如果要按顺序迭代,可以执行以下操作:

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

请注意,在性能方面,这不是最佳选择,但这是您想要一个漂亮的字母显示时的价格。

于 2020-08-11T09:30:56.117 回答
0

截至 2022 年

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

于 2022-01-31T12:21:44.777 回答