javascript之算法优化

阅读更多

正如其他编程语言,代码的写法和算法选用影响javascript的运行时间。与其他编程语言不同的是,javascript可用资源有限,所以优化技术更为重要。

 

一。for,while,do-while循环的性能特性相似,谁也不比谁更快或更慢。除非你要迭代遍历一个属性未知的对象,否则不要使用for-in循环。

 

由于每次迭代操作要搜索实例或原形的属性,for-in 循环每次迭代都要付出更多开销,所以比其他类型

循环慢一些。如果你迭代遍历一个有限的,已知的属性列表,使用其他循环类型更快,可使用如下模式:

 

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

 

 

 

二。改善循环性能的最好方法是减少每次迭代中的运算量,并减少循环迭代次数。

 

//slow
for (var i=0; i < items.length; i++){
process(items[i]);
}
//faster
for (var i=0, len=items.length; i < len; i++){
process(items[i]);
}
//fastest
for (var i=items.length; i--; ){
process(items[i]);
}

 达夫设备是一个循环体展开技术,在一次迭代中实际上执行了多次迭代操作。

 

 

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--]);
}

 

 

三。一般来说,switch总是比if-else更快,但并不总是最好的解决方法。当判断条件较多时,查表法比if-else或者switch更快。

 

//slow
if (value == 0){
return result0;
} else if (value == 1){
return result1;
} else if (value == 2){
return result2;
} else if (value == 3){
return result3;
} else if (value == 4){
return result4;
} else if (value == 5){
return result5;
} else if (value == 6){
return result6;
} else if (value == 7){
return result7;
} else if (value == 8){
return result8;
} else if (value == 9){
return result9;
} else {
return result10;
}

//faster,二分搜索法(此方法适用于需要测试大量数值的情况(相对离散值来说switch 更合适)
if (value < 6){
if (value < 3){
if (value == 0){
return result0;
} else if (value == 1){
return result1;
} else {
return result2;
}
} else {
if (value == 3){
return result3;
} else if (value == 4){
return result4;
} else {
return result5;
}
}
} else {
if (value < 8){
if (value == 6){
return result6;
} else {
return result7;
}
} else {
if (value == 8){
return result8;
} else if (value == 9){
return result9;
} else {
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];

 

 

四。浏览器的调用栈尺寸限制了递归算法在javascript中的应用,栈溢出错误导致其他代码也不能正常执行。如果你遇到一个栈溢出错误,将方法修改为一个迭代算法或者使用制表法可以避免重复工作。

 

 

JavaScript 引擎所支持的递归数量与JavaScript 调用栈大小直接相关。只有Internet Explorer 例外,它的

调用栈与可用系统内存相关,其他浏览器有固定的调用栈限制。大多数现代浏览器的调用栈尺寸比老式浏

览器要大(例如Safari 2 调用栈尺寸是100).当你使用了太多的递归,超过最大调用栈尺寸时,浏览器会出错并弹出以下信息:

 

• Internet Explorer: “Stack overflow at line x”

• Firefox: “Too much recursion”

• Safari: “Maximum call stack size exceeded”

• Opera: “Abort (control stack overflow)”

Chrome 是唯一不显示调用栈溢出错误的浏览器。

 

递归模式一:一个函数调用自身

 

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

 递归模式二:精巧模式,两个函数互相调用对方

 

 

 

 

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

 

 

任何可以用递归实现的算法都可以用迭代实现:

 

//javascript的合并排序算法
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));
}

//uses the same mergeSort() function from previous example
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];
}
虽然迭代版本的合并排序可能比递归版本的慢一些,但它不会像递归版本那样影响调用栈。将递归算法切换为迭代只是避免栈溢出错误的方法之一。

 

 

制表:

制表,通过缓存先前计算结果为后续计算所重复使用,避免了重复工作。这使得制表成为递归算法中有用的技术。

//slow
function factorial(n){
if (n == 0){
return 1;
} else {
return n * factorial(n-1);
}
}
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];
}
var fact6 = memfactorial(6);
var fact5 = memfactorial(5);
var fact4 = memfactorial(4);

 制表过程因每种递归函数而略有不同,但总体上具有相同的模式。为了使一个函数的制表过程更加容易,

你可以定义一个memoize()函数封装基本功能。例如:

function memoize(fundamental, cache){
cache = cache || {};
var shell = function(arg){
if (!cache.hasOwnProperty(arg)){
cache[arg] = fundamental(arg);
}
return cache[arg];
};
return shell;
}
//memoize the factorial function
var memfactorial = memoize(factorial, { "0": 1, "1": 1 });
//call the new function
var fact6 = memfactorial(6);
var fact5 = memfactorial(5);
var fact4 = memfactorial(4);

 

你可能感兴趣的:(javascript,算法,优化,制表,迭代)