这篇是Nicholas讨论如果防止脚本失控的第二篇,主要讨论了如何重构嵌套循环、递归,以及那些在函数内部同时执行很多子操作的函数。
基本的思想和上一节chunk()那个例子一致,如果几个操作没有特定的执行顺序,而且互相不是依赖关系,我们就可以通过异步调用的方式加以执行,不止可
以减少执行的次数,还可以防止脚本失控。本文还介绍了通过memoization技术取代递归的方法。
function bubbleSort(items) {
for (var i = items.length - 1; i >= 0; i--) {
for (var j = i; j >= 0; j--) {
if (items[j] < items[j - 1]) {
var temp = items[j];
items[j] = items[j - 1];
items[j - 1] = temp;
}
}
}
}
回忆一下你在学校学习的计算机知识,你可能记得冒泡排序法是效率最低的排序算法之一,原因是对于一个包含n个元素的数组,必须要进行n的平方次
的循环操作。如果数组中的元素数非常大,那么这个操作会持续很长时间。内循环的操作很简单,只是负责比较和交换数值,导致问题的最大原因在于循环执行的次
数。这会导致浏览器运行异常,潜在的直接结果就是那个脚本失控的警告对话框。
几年前,Yahoo的研究员Julien Lecomte写了一篇题为《
Running CPU Intensive JavaScript Computations in a Web Browser
》的文章,在这篇文章中作者阐述了如何将很大的javaScript操作分解成若干小部分。其中一个例子就是将冒泡排序法分解成多个步骤,每个步骤只遍历一次数组。我对他的代码做了改进,但方法的思路还是一样的:
function bubbleSort(array, onComplete) {
var pos = 0; (function() {
var j, value;
for (j = array.length; j > pos; j--) {
if (array[j] < array[j - 1]) {
value = data[j];
data[j] = data[j - 1];
data[j - 1] = value;
}
}
pos++;
if (pos < array.length) {
setTimeout(arguments.callee, 10);
} else {
onComplete();
}
})();
}
这个函数借助一个异步管理器来实现了冒泡算法,在每次遍历数组以前暂停一下。onComplete()函数会在数组排序完成后触发,提示用户数
据已经准备好。bubbleSort()函数使用了和chunk()函数一样的基本技术(参考我的上一篇帖子),将行为包装在一个匿名函数中,将
arguments.callee传递给setTimeout()以达到重复操作的目的,直至排序完成。如果你要将嵌套的循环拆解成若干个小步骤,以达到
解放浏览器的目的,这个函数提供了不错的指导意见。
相似的问题还包括过多的递归。每个额外的递归调用都会占用更多的内存,从而减慢浏览器的运行。恼人的是,你可能在浏览器发出脚本失控警告之前,就耗尽了系统的内存,导致浏览器处于停止响应的状态。Crockford在博客上曾经对
这个问题
进行过深入的讨论。他当时使用的例子,就是用递归生成一个斐波那契数列。
function fibonacci(n) {
return n < 2 ? n: fibonacci(n - 1) + fibonacci(n - 2);
};
按照Crockford的说法,执行fibonacci(40)这条语句将重复调用自身331160280次。避免使用递归的方案之一就是使用
memoization
技术,这项技术可以获取上一次调用的执行结果。Crockford介绍了下面这个函数,可以为处理数值的函数增加这项功能:
function memoizer(memo, fundamental) {
var shell = function (n) {
var result = memo[n];
if (typeof result !== 'number') {
result = fundamental(shell, n);
memo[n] = result;
}
return result;
};
return shell;
};
他接下来将这个函数应用在斐波那契数列生成器上:
var fibonacci = memoizer([0, 1],
function(recur, n) {
return recur(n - 1) + recur(n - 2);
});
这时如果我们再次调用fibonacci(40),只会重复调用40次,和原来相比提高得非常多。memoization的原理,概括起来就一句话,同样的结果,你没有必要计算两次。如果一个结果你可能会再次使用,把这个结果保存起来,总比重新计算一次来的快。
最后一个可能让函数执行缓慢的原因,就是我们之前提到过的,函数里面执行了太多的内容,通常是因为使用了类似下面的开发模式:
function doAlot() {
doSomething();
doSomethingElse();
doOneMoreThing();
}
在这里要执行三个不同的函数,请注意,无论是哪个函数,在执行过程中都不依赖其他的函数,他们在本质是相对独立的,只是需要在一个特定时间逐一执行而已。同样,你可以使用类似chunk()的方法来执行一系列函数,而不会导致锁定浏览器。
function schedule(functions, context) {
setTimeout(function() {
var process = functions.shift();
process.call(context);
if (functions.length > 0) {
setTimeout(arguments.callee, 100);
}
},100);
}
schedule函数有两个参数,一个是包含要执行函数的数组,另外一个是标明this所属的上下文对象。函数数组以队列方式实现,Timer事件每次触发的时候,都会将队列最前面的函数取出并执行,这个函数可以通过下面的方式执行一系列函数:
schedule([doSomething, doSomethingElse, doOneMoreThing], window);
很希望各个JavaScript的类库都增加类似这样的进程处理函数。YUI在3.0时就已经引入了
Queue
对象,可以通过timer连续调用一组函数。
无
论现有的技术可以帮助我们将复杂的进程拆分到什么程度,对于开发者来说,使用这种方法来理解并确定脚本失控的瓶颈是非常重要的。无论是太多的循环、递归还
是其他的什么,你现在应该知道如果处理类似的情况。但要记住,这里提到的技术和函数只是起到抛砖引玉的作用,在实际的应用中,你应该对它们加以改进,这样
才能发挥更大的作用。
相关推荐
to-date JavaScript programming techniquesContinuing in the superlative tradition of the first three editions, Beginning JavaScript, Fourth Edition, gets you up to speed on all the new advances in ...
Next, you will move on to learn about DOM optimization, JavaScript promises, and web workers to better break up your large codebase. You will also learn about JavaScript performance on mobile ...
Zakas 于 2009 年 1 月 20 日在个人网站上发表的《Speed up your JavaScript, Part 2》。本文主要讨论了如何重构嵌套循环、递归,以及那些在函数内部同时执行很多子操作的函数,以提高 JavaScript 的运行速度。 ...
This completely updated second edition covers everything you need to know to get up-to-speed with JavaScript development and add dynamic enhancements to web pages, right from the basics. As well as ...
This completely updated second edition covers everything you need to know to get up-to-speed with JavaScript development and add dynamic enhancements to web pages, right from the basics. As well as ...
Speed up and simplify app development with jQuery Quickly retrieve data from a server using AJAX requests Adapt your app for mobile devices with jQuery Mobile Build Windows 8 apps using HTML, CSS, and...
This book will bring programmers and non-technical professionals, including casual programmers and scripters, painlessly up to speed on all aspects of mastering JavaScript. Key topics include ...
当谈到 JS 性能的时候,Zakas差不多就是你要找的,2010年六月他在Google Tech Talk发表了名为《Speed Up Your Javascript》的演讲。 但 Javascript 性能优化绝不是一种书面的技术,Nicholas 的技术演进列出了10条...
who are just learning the language and want to come up to speed quickly. In either case we assume you have at least a basic background in programming. Chapter 1, in particular, assumes you are coming...
原文标题:Speed up your JavaScript, Part 1 原文作者:Nicholas C. Zakas 在我 上一篇帖子 (译文 ) 中,谈到了各个浏览器究竟会在什么情况下弹出脚本失控提示,对于Internet Explorer 来说,当浏览器执行了数量...
You will find plenty of fully explained code and ample screenshots in the book to ease and speed up your understanding. This book is for developers, ideally with web development experience, who are ...
You will find plenty of fully explained code and ample screenshots in the book to ease and speed up your understanding. This book is for developers, ideally with web development experience, who are ...
Get up to speed with TypeScript, a powerful typed superset of JavaScript that compiles to plain JavaScript Take full control of how your data is rendered and updated upon data changes Build powerful ...
Get up to speed with TypeScript, a powerful typed superset of JavaScript that compiles to plain JavaScript Take full control of how your data is rendered and updated upon data changes Build powerful ...