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

时间:2008-11-11 12:00:37

标签: javascript for-loop

Javascript中的“for ... in”循环是否按照声明的顺序循环遍历hashtables / elements?是否存在按顺序执行的浏览器?
我想要使​​用的对象将被声明为一次,永远不会被修改。

假设我有:

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

我进一步使用它们:

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

在大多数体面的浏览器中,我能指望'A:“你好”总是先来'B:'世界“吗?

9 个答案:

答案 0 :(得分:201)

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"

这方面的技术性不如此可能在任何时候发生变化那么重要。不要依赖这样的事情。

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

答案 1 :(得分: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);

gisttest 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] 

答案 2 :(得分:26)

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

  

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

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

  

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

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

如果您希望定义订单,则需要实现定义它的内容,例如在使用它访问对象之前排序的键数组。

答案 3 :(得分:10)

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

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

答案 4 :(得分: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的讨论有任何迹象,那么改变规范来修复迭代顺序的愿望似乎是开发人员非常普遍的愿望。

答案 5 :(得分:3)

在IE6中,订单无法保证。

答案 6 :(得分: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。

答案 7 :(得分:1)

这本身不回答问题,但提供了解决基本问题的方法。

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

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

现在,您需要确保密钥是唯一的(假设这对您来说也很重要。同样,直接寻址更改,以及(... in ...)现在返回索引为& #39;键&#39;

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

> for (let index in myArray) {console.log(myArray[index].value);}
0
1
通过for (...in...) addressing in order上的JDQ(@JDQ)查看笔CodePen

答案 8 :(得分:0)

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

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

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

请注意,从性能角度来看,这并不是最佳选择,但这就是您想要漂亮的字母显示时的价格。