对象属性无序性
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循环来枚举对象属性应当与顺序无关
-
如果聚集运算字典中的数据,确保聚集操作与顺序无关
-
使用数组而不是字典来存储有序集合