你的位置:首页 > Java教程

[Java教程][Effective JavaScript 笔记]第46条:使用数组而不要使用字典来存储有序集合


对象属性无序性

js对象是一个无序属性集合。

var obj={};obj.a=10;obj.b=30;

属性a和属性b并没有谁前谁后之说。for...in循环,先输出哪个属性都有可能。
获取和设置不同的属性与顺序无关,都会以大致相同的效率产生相同的结果。
也就是说访问属性a和访问属性b,没有哪个访问更快之说。ES标准并未规定属性存储的任何特定顺序,甚至于枚举对象也未涉及。for...in循环会挑选一定的顺序来枚举对象的属性,标准允许js引擎自由选择一个顺序,它们的选择会微妙地改变程序行为。
如要求一个对象表示一个从字符串到值的有序映射,创建一个有序的报表。

function report(highScores){  var res='';  var i=1;  for(var name in highScores){    res+=i+'. '+highScores[name].name+':'+highScores[name].points+'\n';    i++;  }  return res;}report([{name:'张三',points:1110111},    {name:'李四',points:1110102},    {name:'王五',points:1110911}]);/*预期的结果"1. 张三:11101112. 李四:11101023. 王五:1110911"*//*实际的结果 chrome,ff,ie"1. 张三:11101112. 李四:11101023. 王五:1110911"*/

上面代码在测试的几个环境中表现顺序和索引相符,但一些其它的环境可以选择以不同的顺序来存储和枚举对象的属性,所以report有可能导致产生不同的字符串,得到不正确的报表。
程序对对象枚举的顺序依赖并不是显式地。如果没有在多个js环境中测试过你的代码,那么你的程序有可能因为for...in循环的不同输出而导致改变。

依赖数据顺序

如果对于数据结构中的条目顺序有强依赖,那么就优先考虑数组而不是字典。如上面的report函数如果接收的是一个数组而不是一个对象,那么可以用for来循环,可以保证在所有环境顺序都是一致正确的。

function report(highScores){  var res='';  for(var i=0,n=highScores.length;i < n;i++){    var score=highScores[i];    res+=(i+1)+'. '+score.name+':'+score.points+'\n';  }  return res;}report([{name:'张三',points:1110111},    {name:'李四',points:1110102},    {name:'王五',points:1110911}]);//"1. 张三:11101112. 李四:11101023. 王五:1110911"

通过接收一个对象数组,每个对象包含有name和points属性,上面的代码可以按0~highScores.length-1的顺序遍历所有的元素。

浮点型运算

假设有一个映射标题和等级的电影字典。

var ratings={  'Good Will Hunting':0.8,  'Mystic River':0.7,  '21':0.6,  'Doubt':0.9};

浮点型算术运算的四舍五入会导致对计算顺序依赖。详细见《第2条:理解JavaScript的浮点数》。当组合未定义顺序的枚举时,可能会导致循环不可预知。

var total=0,count=0;for(var key in ratings){  total+=ratings[key];  count++;}total/=count;total;//chrome里:0.7499999999999999

在流行的js环境实际上使用不同的顺序执行这个循环。一些环境按照下面的顺序来枚举对象的key,得到下面这个值。

(0.8+0.7+0.6+0.9)/4 //0.75

有些环境总是先枚举潜在的数组索引,然后才是其他key。电影21是可以作为数组的索引的整数值,它首先被枚举,得到下面的结果。

(0.6+0.8+0.7+0.9)/4 //0.7499999999999999

可以看到上面的chrome就是先枚举潜在的数组索引。

整数计算浮点型

对于浮点数的计算,可以把浮点数转化为整数,然后再转化回浮点数。整数的计算顺序可以是任意顺序的。所以对象的属性值的列举顺序并不重要。代码如下

(8+7+6+9)/4/10 //0.75(6+8+7+9)/4/10 //0.75

提示

  • 使用for...in循环来枚举对象属性应当与顺序无关

  • 如果聚集运算字典中的数据,确保聚集操作与顺序无关

  • 使用数组而不是字典来存储有序集合