javascript 性能分析:算法和流程控制

来源:互联网 发布:php基础知识点 编辑:程序博客网 时间:2024/05/16 05:33

算法和流程控制

    代码整体结构是执行速度的决定元素之一。代码少不一定运行快,代码多不一定运行慢。

一,循环

for (var i=0; i < 10; i++){
//loop body
}

---------------------

var i = 0;
while(i < 10){
//loop body
i++;
}
--------------------

var i = 0;
do {
//loop body
} while (i++ < 10);

-------------------

for (var prop in object){
//loop body
}

上面四种循环相对而言 for-in循环比较慢点 其他的看场景评价。for-in每次循环操作都要搜索实例或原形的属性,所以除非你对数据不详进行遍历 否则不要使用for-in。

var props = ["prop1", "prop2"],
i = 0;
while (i < props.length){
process(object[props[i]]);
}

while循环用于遍历这个属性并处理对应的对象成员而不是遍历对象的每个属性。其他三种循环如何选择 有2个元素可以参考:每次迭代干什么  和 迭代的次数。

通过减少2个元素的一个或全部 可以很好的提高循环的性能。

减少迭代的工作量

常规循环:

for (var i=0; i < items.length; i++){
process(items[i]);
}
var j=0;
while (j < items.length){
process(items[j++]]);
}
var k=0;
do {
process(items[k++]);
} while (k < items.length);
它们每次循环执行下面步骤:

1.控制条件中读一次的属性   items.length

2.在控制条件中执行一次比较  i < items.length

3.比较操作 查看条件控制体结果是不是true   i < items.length == true

4.一次自加操作 i++

5.一次数组操作  items[i]

6.一次函数调用  process(items[i])

减少操作次数:不变的属性访问用局部变量替代。

//minimizing property lookups
for (var i=0, len=items.length; i < len; i++){
process(items[i]);
}
var j=0,
count = items.length;
while (j < count){
process(items[j++]]);
}
var k=0,
num = items.length;
do {
process(items[k++]);
} while (k < num);

改变他们的循环顺序 如果他们结果和顺序无关的话:

for (var i=items.length; i--; ){
process(items[i]);
}
var j = items.length;
while (j--){
process(items[j]]);
}
var k = items.length-1;
do {
process(items[k]);
} while (k--);

使用倒序循环使用减法这样每个控制条件只是简单的和零比较 任何非零的数字自动转换为true。减少了一次的比较操作。与原始版本的操作步骤比较:

1.在控制条件中进行一次比较  i == true

2.一次减法操作  i--

3. 一次数组查询  items[i]

4.一次函数调用 process(items[i])


减少迭代次数:经典达夫循环

var iterations = Math.floor(items.length / 8),
startAt = items.length % 8,
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);

基本理念:每次循环中最多可8次调用process()函数。循环迭代次数为元素总数除以8。总数不一定是8的倍数 所以

startAt 变量存放余数,指出第一次循环中应该执行多少次process() 比如12个元素 第一次循环调用4次process() 第二次循环调用8次process()  用2次循环替换12次循环。

改变版:这样速度更快

var i = items.length % 8;
while(i){
process(items[i--]);
}
i = Math.floor(items.length / 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--]);

}

上面的减少次数的循环只是在大量(1000次循环以上)循环效果才明显。

基于函数的迭代

原生态的:

items.forEach(function(value, index, array){
process(value);
});

其他迭代:

//YUI 3
Y.Array.each(items, function(value, index, array){
process(value);
});
//jQuery
jQuery.each(items, function(index, value){
process(value);
});
//Dojo
dojo.forEach(items, function(value, index, array){
process(value);
});

//Prototype
items.each(function(value, index){
process(value);
});
//MooTools
$each(items, function(value, index){
process(value);
});

if else 和 switch 比较

条件多侧重用 switch (三层以上)条件少一般用if else (三层以下)

if else 一般适用2个离散的值或判断几个不同的值域 如果是多于2个离散的值 switch更理想。

if else 优化:

if (value < 5) {
//do something
} else if (value > 5 && value < 10) {
//do something
} else {
//do something
}

将最常见的条件放在第一个

查表法:

当有大量的离散值的时候 用 if else 或 switch 还是不理想 这个时候用 查表法就好点。

switch(value){
case 0:
return result0;
case 1:
return result1;
case 2:
return result2;
case 3:
return result3;

case 4:
return result4;
case 5:
return result5;
case 6:
return result6;
case 7:
return result7;
case 8:
return result8;
case 9:
return result9;
default:
return result10;
}

查表法的一种形式:

//define the array of results
var results = [result0, result1, result2, result3, result4, result5, result6, result7, result8, result9, result10]
//return the correct result
return results[value];

递归:调用栈限制 调用太多的递归 浏览器很容易报错

function factorial(n){
if (n == 0){
return 1;
} else {
return n * factorial(n-1);
}
}

所以最好try catch

try {
recurse();
} catch (ex){
alert("Too much recursion!");
}

---------------递归模式:

function recurse(){
recurse();
}
recurse();

--------------

function first(){
second();
}
function second(){
first();
}
first();

太多层次 很容易搞成死循环 应该用迭代替换

合并排序算法:

function merge(left, right){
var result = [];
while (left.length > 0 && right.length > 0){
if (left[0] < right[0]){
result.push(left.shift());
} else {
result.push(right.shift());
}
}
return result.concat(left).concat(right);
}
function mergeSort(items){
if (items.length == 1) {
return items;
}
var middle = Math.floor(items.length / 2),
left = items.slice(0, middle),

right = items.slice(middle);
return merge(mergeSort(left), mergeSort(right));
}

迭代实现:

function mergeSort(items){
if (items.length == 1) {
return items;
}
var work = [];
for (var i=0, len=items.length; i < len; i++){
work.push([items[i]]);
}
work.push([]); //in case of odd number of items
for (var lim=len; lim > 1; lim = (lim+1)/2){
for (var j=0,k=0; k < lim; j++, k+=2){
work[j] = merge(work[k], work[k+1]);
}
work[j] = []; //in case of odd number of items
}
return work[0];
}

制表 Memoization

避免重复工作 也是提高速度的一种途径。制表 通过缓存先前计算结果为后续计算重复使用 这使得制表成为递归算法中有用的技术。

var fact6 = factorial(6);
var fact5 = factorial(5);
var fact4 = factorial(4);

像上面的重复调用递归函数 明显重复

function memfactorial(n){
if (!memfactorial.cache){
memfactorial.cache = {
"0": 1,
"1": 1
};
}
if (!memfactorial.cache.hasOwnProperty(n)){
memfactorial.cache[n] = n * memfactorial (n-1);
}
return memfactorial.cache[n];
}

封装一下:

function memoize(fundamental, cache){
cache = cache || {};
var shell = function(arg){
if (!cache.hasOwnProperty(arg)){
cache[arg] = fundamental(arg);
}
return cache[arg];
};
return shell;
}

实例:

var memfactorial = memoize(factorial, { "0": 1, "1": 1 });
//call the new function
var fact6 = memfactorial(6);
var fact5 = memfactorial(5);
var fact4 = memfactorial(4);


 




原创粉丝点击