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

cha*_*rit 196 javascript for-loop

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

假设我有:

var myObject = { A: "Hello", B: "World" };
Run Code Online (Sandbox Code Playgroud)

我进一步使用它们:

for (var item in myObject) alert(item + " : " + myObject[item]);
Run Code Online (Sandbox Code Playgroud)

在大多数体面的浏览器中,我可以期待'A:'你好''总是先来'B:'世界"吗?

Bor*_*gar 206

引用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"
Run Code Online (Sandbox Code Playgroud)

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

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

  • "如果订单对您很重要,请使用数组":当您使用JSON时怎么样? (19认同)
  • @ HM2K,同样的事情,spec说"对象是零个或多个名称/值对的无序集合." JSON不是JavaScript:服务器不需要(也可能不会)尊重您提供的订单. (11认同)
  • Firefox自21版以来似乎不再尊重插入顺序了. (7认同)
  • 并不是的.Chrome与其他浏览器的订单不同:http://code.google.com/p/v8/issues/detail?id = 164 (3认同)
  • 在ES2015中,此答案是错误的。 (2认同)
  • @GrégoryNEUT 是的,但仅对于某些方法,“EnumerateObjectProperties”和“EnumerableOwnPropertyNames”顺序取决于实现。例如,地图按照插入顺序进行迭代。Object.keys 顺序显然仍然依赖于实现。 (2认同)

dvd*_*rgn 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);
Run Code Online (Sandbox Code Playgroud)

当前浏览器中的要点测试

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"]
Run Code Online (Sandbox Code Playgroud)

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"]
Run Code Online (Sandbox Code Playgroud)

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] 
Run Code Online (Sandbox Code Playgroud)

  • 这有什么问题?正如规范所说,它是一个无序的键/值对列表. (15认同)
  • @boxed:将对象视为哈希映射(table/whatever).在大多数语言(Java,Python等)中,这些类型的数据结构都没有排序.因此,在JavaScript中这也是同样的情况并不奇怪,它肯定不会使规范错误或愚蠢. (10认同)
  • 老实说,我没有看到这个答案(对不起).迭代属性的顺序是实现细节,浏览器使用不同的JavaScript引擎,因此预期订单会有所不同.这不会改变. (9认同)
  • nickf:实现是正确的,因为他们做了规范所说的.我想每个人都同意javascript规范是......好吧,我不想用"错误"这个词,"非常愚蠢和讨厌"怎么样?:P (5认同)
  • 在现有技术实现中,由于受到真实数组的支持,您将按数值顺序接收整数属性,并且由于隐藏的类/形状公式,将按插入顺序接收命名属性.可能会有所不同的是它们首先列出整数属性还是首先列出命名属性.添加`delete`很有意思,因为至少在V8中,它会立即导致对象被哈希表备份.但是,V8中的哈希表按插入顺序存储.这里最有趣的结果是IE,我想知道他们做了什么样的丑陋来解决这个问题... (2认同)

Tom*_*lak 26

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

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

第4.3.3节("对象"的定义):

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

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

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


Ada*_*ght 10

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

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

  • 这就是他提出问题的原因,而不仅仅是假设:p (2认同)

Bre*_*mir 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
}
Run Code Online (Sandbox Code Playgroud)

如果http://code.google.com/p/v8/issues/detail?id=164上的讨论有任何迹象,那么更改规范以修复迭代顺序的愿望似乎是开发人员的一个普遍愿望。