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