欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

javascript之算法优化

程序员文章站 2022-05-19 18:00:46
...

正如其他编程语言,代码的写法和算法选用影响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);