<<高性能JavaScript>>中有一章提到对循环进行优化,策略之一就是减少循环迭代次数:
大家可能都听过C语言实现的“达夫设备”算法(Duff's Device),Jeff Greenberg被认为是将”达夫设备“代码从原始的C实现移植到JavaScript中的第一人,一个典型实现如下:
//为了方便调用,把它封装成一个函数
function duff(items) {
var len = items.length, //缓存局部变量
iterations = Math.floor(len / 8), //商数,存放duff迭代次数
startAt = len % 8, //余数,存放duff一次迭代调用process的次数
i = 0;
do {
switch(startAt) {
case 0:
process(items[i++]);
case 7:
process(items[i++]);
case 6:
process(items[i++]);
case 5:
process(items[i++]);
case 4:
process(items[i++]);
case 3:
process(items[i++]);
case 2:
process(items[i++]);
case 1:
process(items[i++]);
}
startAt = 0;
} while(iterations--); //书上是--iterations,貌似不对吧,应该是iterations--
};
Duff’s Device背后的基本理念是:每次循环中最多可调用8次process()。循环的迭代次数iterations为总数除以8,startAt用来存放余数,表示一次循环中应调用多少次process()。
假设我们定义process函数为:
function process(item) {
alert(item);
};
调用一下duff函数:
duff([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]); //依次弹出1~12
//在这个调用过程中,我们可以知道iterations为1,startAt为4,也就是说,循环了两次,第一次循环调用process函数4次,第二次循环调用了8次。
可见,可以满足我们的遍历需求,而据书中介绍,如果迭代次数超过1000次,duff遍历算法的执行效率将明显提升。
据书中介绍,该算法有一个稍快的版本如下:(取消了switch语句,并将余数处理和主循环分开)
function duff2(items) {
var len = items.length,
i = len % 8;
while(i) {
process(items[i--]);
};
i = Math.floor(len / 8);
while(i){
process(items[i--]);
process(items[i--]);
process(items[i--]);
process(items[i--]);
process(items[i--]);
process(items[i--]);
process(items[i--]);
process(items[i--]);
}
};
<<高性能JavaScript>>这本书中是这样写这个算法的,不过仔细一看就知道,这个算法明显有问题,它无法遍历一个完整的数组,正确的算法应该是:
function duff2(items) {
var len = items.length,
t = 0,
i = len % 8;
while(i--) {
process(items[t++]);
};
i = Math.floor(len / 8);
while(i--){
process(items[t++]);
process(items[t++]);
process(items[t++])
process(items[t++]);
process(items[t++]);
process(items[t++]);
process(items[t++]);
process(items[t++]);
}
};
这里有个老外写的Duff's device各种优化方法
这个算法对于处理数据量比较大的数组,还是很有执行效率的,比如对于处理省、市、区等级联下拉框的时候,如果你用数组来存放这些数据的话,就可以使用这个算法来遍历数组,可以感觉明显快很多。
还有一种优化数组遍历的方法,“分时处理”,这里有个不错的解析:
这个方法用着有点不放心(毕竟内部存在着定时器,搞不好数组还没遍历并更新完成,后面的代码都快执行完了,如果这个时候,后面的代码需要使用这个被遍历的数组,那更悲剧了,拿到的不一定是新数组),呵呵,视具体情况而定,比如说:
var a = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12];
//我现在想用分时处理的方式来遍历这个数组
chunk(a, function(v, i) {
a[i] = v + 1; // 假如我想在这里这个数组的每一项都+1
}, window);
//那么假如接下来,我想立即使用这个已经更新过的数组
console.log(a.toString()); //结果得到啥?
setTimeout(function() {
console.log(a.toString()); //这里又是啥?
}, 100);
//未完待续,临时有事,晚上回去再继续~~~
先写到这里,不保证文章的正确性,仅供参考!