如果是数字,则Chrome重新排序对象键,是正常/预期的

时间:2010-07-06 13:51:30

标签: javascript json google-chrome javascript-objects

我注意到某些代码会评估电子商务网站的某些尺码并将其输出到屏幕上,这会使Chrome中的订单混乱。

给出的JSON可以是:

{
  "7": ["9149", "9139", "10455", "17208"],
  "7.5": ["9140", "9150", "10456", "17209"],
  "8": ["2684", "9141", "10457", "17210"],
  "8.5": ["9142", "10444", "10458", "17211"],
  "9": ["2685", "9143", "10459", "17212"],
  "9.5": ["10443", "9144", "10460", "17213"]
}

...将大小增加一半。

转换为对象并迭代键后,自然顺序得到尊重,它们就会出现:

  

7,7.5,8,8.5等。

但仅在Chrome中,“看起来像圆形数字始终的键首先从对象中出来,因此for ... in循环的输出是:

  

7,8,9,7.5,8.5,9.5 ......

Object.keys(sizes); // ["7", "8", "9", "7.5", "8.5", "9.5"]

以下是测试用例:https://jsfiddle.net/wcapc46L/1/

它只影响整数,似乎Webkit / Blink有一个优化更喜欢数字的对象属性,也许它与分支预测或其他什么有关。

如果在对象键前加上任何字符,则顺序不受影响并按预期工作 - FIFO

我想我记得读到对象的属性顺序没有任何保证,但与此同时,这是极端烦人的,并且会为Chrome用户单独修复它会造成相当大的努力。

有什么想法吗?这可能是一个会被修复的错误吗?

编辑此外,我现在发现这是v8错误跟踪器上的一个问题:

http://code.google.com/p/v8/issues/detail?id=164

看起来Blink不想解决这个问题,并且仍然是唯一能够做到这一点的浏览器。

  

更新任何哈希表优化webkit / blink已经进入gecko(FF 27.0.1) - http://jsfiddle.net/9Htmq/结果为7,8,9,7.5,8.5,9.5。在密钥返回正确/预期的订单之前应用_

     

2017年更新人们仍在对此进行推广和编辑 - 它似乎不会影响Map / WeakMapSet等(如更新所示)主要例子)

7 个答案:

答案 0 :(得分:21)

这是v8处理关联数组的方式。已知问题Issue 164但遵循规范,因此标记为“按预期工作”。循环遍历关联数组没有必需的顺序。

一个简单的解决方法是在数字值前加上字母,例如:'size_7':['9149','9139']等。

该标准将在下一个ECMAScript规范中发生变化,迫使[chrome]开发人员更改此内容。

答案 1 :(得分:1)

Chrome似乎将整数字符串视为用作索引/属性名称时的数字类型。

我认为依赖于Javascript实现来保持在某些情况下是对象属性的顺序,在其他情况下(当然是使用chrome)数组索引,显然是一种不安全的方法,并且可能没有定义枚举的顺序在规范中。我建议在JSON中添加一个指示排序顺序的附加属性:

{
    "7":{"sortOrder":1,"data":["9149","9139","10455","17208"]},
    "7.5":{"sortOrder":2,"data":["9140","9150","10456","17209"]}
    //etc
}

答案 2 :(得分:1)

它们被视为字符串,因为它们字符串。我最好的建议是在所有按键中使用相同的“精度”。

{"7.0":["9149","9139","10455","17208"],"7.5":["9140","9150","10456","17209"],"8.0":["2684","9141","10457","17210"],"8.5":["9142","10444","10458","17211"],"9.0":["2685","9143","10459","17212"],"9.5":["10443","9144","10460","17213"]}

所以,“8.0”而不是“8”等等。

即便如此,也没有任何保证,但它们更有可能以相同的顺序出现。

为了更好的保证,请根据键执行排序,将值按排序顺序放入数组中。

答案 3 :(得分:1)

当迭代对象的属性时,在ECMAScript规范中将顺序指定为未定义,并且不应依赖在某些环境中可能观察到的任何顺序。如果您需要订购,请使用Array

答案 4 :(得分:0)

我认为你不能称之为bug。就像你自己说的那样,对于如何对对象的属性进行排序没有任何保证。

答案 5 :(得分:0)

我找到了一个简单的工作,使用underscore.js

myArray = _.sortBy(myArray, function(num){ return Math.ceil(num); });

耶! myArray在所有浏览器中都恢复正常顺序。

答案 6 :(得分:0)

我的键对我来说太重要了,无法将它们转换为字符串。相反,我创建了另一个仅维护键顺序的数组。

<?php 
$origArray = valueReturnedFromFunction();
$preservedOrder = array_keys($origArray);
?>
<script>
var origArray = <?php echo json_encode($origArray)?>;
var preservedOrder = <?php echo json_encode($preservedOrder )?>;

for(i in preservedOrder){
    var item = origArray[i];
    ...
}
</script>