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

假设我有:

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

我进一步将它们用于:

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

在大多数像样的浏览器中,“A:“Hello”总是出现在“B:“World”之前吗?


当前回答

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

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

其他回答

引用John Resig的话:

Currently all major browsers loop over the properties of an object in the order in which they were defined. Chrome does this as well, except for a couple cases. [...] This behavior is explicitly left undefined by the ECMAScript specification. In ECMA-262, section 12.6.4: The mechanics of enumerating the properties ... is implementation dependent. However, specification is quite different from implementation. All modern implementations of ECMAScript iterate through object properties in the order in which they were defined. Because of this the Chrome team has deemed this to be a bug and will be fixing it.

所有浏览器都尊重定义顺序,除了Chrome和Opera,它们对每个非数字属性名都遵守定义顺序。在这两个浏览器中,属性被按顺序拉到第一个非数值属性之前(这与它们如何实现数组有关)。Object的顺序是一样的。还有钥匙。

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

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"

这方面的技术细节并不重要,重要的是这一点随时都可能改变。不要指望事情会一直这样下去。

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

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

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

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

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

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

一年后再碰这个……

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

来自ECMAScript语言规范,第12.6.4节(关于..在循环):

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

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

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

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

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