`

如何提升JavaScript的运行速度之函数篇(二)

 
阅读更多

这篇是Nicholas讨论如果防止脚本失控的第二篇,主要讨论了如何重构嵌套循环、递归,以及那些在函数内部同时执行很多子操作的函数。基本的思 想和上一节trunk()那个例子一致,如果几个操作没有特定的执行顺序,而且互相不是依赖关系,我们就可以通过异步调用的方式加以执行,不止可以减少执 行的次数,还可以防止脚本失控。本文还介绍了通过memoization技术取代递归的方法。

【原文标题】Speed up your JavaScript, Part 2
【原文作者】Nicholas C. Zakas

以下是对原文的翻译:

上周我在《too much happening in a loop 》(译文:如何提升JavaScript的运行速度(循环篇) )这篇文章中介绍了JavaScript运行时间过长的第一个原因。相似的情况有时也出现在函数的定义上,函数也可能因为使用不当而过载使用。通常情况是函数内包含了过多的循环(不是在循环中执行了过多的内容),太多的递归,或者只不过是太多不相干但又要一起执行的操作。

太 多的循环经常是以嵌套的形式出现,这种代码会一直占用JavaScript引擎直至循环结束。这方面有一个非常著名的例子,就是使用冒泡算法排序。由于 JavaScript有内置的sort()方法,我们没有必要使用这种方式进行排序,但我们可以借助这个算法理解嵌套循环占用资源的症结所在,从而避免类 似情况的发生。下面是一个在JavaScript使用冒泡排序法的典型例子:
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连续调用一组函数。

无 论现有的技术可以帮助我们将复杂的进程拆分到什么程度,对于开发者来说,使用这种方法来理解并确定脚本失控的瓶颈是非常重要的。无论是太多的循环、递归还 是其他的什么,你现在应该知道如果处理类似的情况。但要记住,这里提到的技术和函数只是起到抛砖引玉的作用,在实际的应用中,你应该对它们加以改进,这样 才能发挥更大的作用。
分享到:
评论

相关推荐

    如何提升JavaScript的运行速度(函数篇).doc

    如何提升JavaScript的运行速度(函数篇) 本文译自 Nicholas C. Zakas 于 2009 年 1 月 20 日在个人网站上发表的《Speed up your JavaScript, Part 2》。本文主要讨论了如何重构嵌套循环、递归,以及那些在函数内部...

    如何提升JavaScript的运行速度(递归篇)

    JavaScript 运行速度优化(递归篇) 本文主要介绍了如何提升 JavaScript 的运行速度,特别是针对递归算法的优化方法。递归是 JavaScript 中的一个性能杀手,它可以让浏览器变得越来越慢,直到死掉或者莫名其妙的...

    如何提升JavaScript的运行速度(循环篇).doc

    总之,提升JavaScript的运行速度需要从多个方面进行考虑,包括循环结构的优化、异步处理、减少DOM操作、使用内置函数和优化数据结构等。通过这些策略,可以显著提高代码执行效率,改善用户体验,同时避免浏览器因...

    JavaScript 提升运行速度之循环篇 译文

    为了提升JavaScript程序的运行速度,特别是针对循环操作的优化,我们有必要了解那些会拖慢JavaScript脚本运行的不良代码实践,并了解如何使用现代JavaScript技术来避免这些问题。 首先,我们来分析一下Nicholas C. ...

    【合集】优化和提高javascript的运行速度

    为了达到优化和提高JavaScript运行速度的目的,有许多策略和技术可以应用,以下将详细阐述这些知识点。 首先,要确保JavaScript的源码尽可能地高效和简洁。可以通过移除不必要的空格、换行和注释来达到这一点。另一...

    一些公用的javascript函数

    了解V8引擎的工作原理,以及如何利用`for-of`循环、阵列的`.length`属性、尾调用优化等技巧,可以提高JavaScript代码的运行速度。 以上只是JavaScript众多知识点的一部分,实际的“一些公用的javascript函数.txt”...

    JavaScript函数节流和函数防抖之间的区别.docx

    JavaScript函数节流和防抖是两种常见的性能优化技术,它们主要用于限制函数在特定时间段内的执行频率,以提高应用的响应速度和用户体验。这两种技术在处理高频率触发的事件,如页面滚动、窗口大小改变、输入框实时...

    javascript 自定义eval函数实现

    总之,理解并自定义`eval()`函数对于提升JavaScript编程的安全性和灵活性至关重要。通过限制执行环境、使用安全的替代方法以及在适当的情境下使用,我们可以充分利用这个功能强大的工具,同时避免潜在的风险。在实际...

    一个更高效的JavaScript版trim函数.rar

    2. **性能对比**:通常,优化后的函数会包含性能测试代码,用来比较其与原生`trim()`函数的运行速度。这可能涉及到`Date.now()`或`performance.now()`等时间戳函数,用于测量执行时间。 3. **兼容性考虑**:优化版...

    自定义函数替换现有函数

    1. **性能优化**:如果默认的内置函数或库函数在某些场景下性能不佳,可以编写自定义函数来提高运行速度。例如,Python中的内建函数`range()`在处理大规模序列时可能较慢,可以编写一个自定义迭代器来优化。 2. **...

    《JavaScript内核系列》和《JavaScript面向对象基础》

    V8引擎是Chrome和Node.js中的高性能JavaScript引擎,它使用即时编译(JIT)技术提高运行速度。 2. 垃圾回收:JavaScript自动管理内存,通过垃圾回收机制来回收不再使用的内存。常见的垃圾回收算法有标记清除、引用...

    High Performance JavaScript(高性能Javascript编程) 【中英文对版】

    作者通过自身的实践经验,揭示了JavaScript编程中的诸多关键技巧和策略,帮助读者理解并应用这些知识来提升程序的运行速度。 1. **内存管理**:书中会详细讲解JavaScript内存分配和垃圾回收机制,包括闭包、作用域...

    JavaScript语言精粹.修订版 Javascript:The Good Parts 中英 pdf

    5. **错误处理**:JavaScript提供了try...catch语句来处理运行时错误。理解如何有效地捕获和处理错误是编写健壮代码的关键。 6. **异步编程**:JavaScript是单线程的,因此异步编程模型(如回调函数、Promise和...

    javascript经典特效---测试打字速度.rar

    JavaScript经典特效——测试打字速度,是一个常见的网页交互功能,常用于评估用户输入速度和准确性。这个主题涉及到了JavaScript的基础语法、DOM操作、事件监听、时间处理等多个知识点,接下来我们将详细探讨这些...

    前台javascript速度优化

    本文将深入探讨"前台JavaScript速度优化"的相关知识点,包括但不限于代码优化策略、工具应用以及Ajax性能提升。 一、代码优化策略 1. 减少DOM操作:频繁的DOM操作会降低页面性能,因为每次操作都需要浏览器重新...

    JavaScript语言精粹(高清电子版)和高性能JavaScript 双语版

    JavaScript,作为全球最广泛使用的编程语言之一,是创建交互式网页和动态应用的关键工具。它在Web开发领域占据着核心地位,特别是在前端开发中。本文将深入探讨标题和描述中提及的两本书籍《JavaScript语言精粹》和...

    Javascript基础教程之函数对象和属性

    在JavaScript编程语言中,函数、对象以及属性是三个核心概念,它们共同构建了JavaScript的基础结构和运行机制。函数是JavaScript中执行特定任务的代码块,对象则是一种复合数据类型,包含了数据和操作这些数据的方法...

    JavaScript编辑器有提示

    二、JavaScript编辑器的类型 1. 基础文本编辑器:如Notepad++、Sublime Text等,虽然功能相对简单,但通过安装插件也能提供基本的JavaScript提示。 2. 集成开发环境(IDE):如Visual Studio Code、WebStorm、...

    xTool javascript函数库 V1.0.0

    **xTool JavaScript函数库 V1.0.0详解** xTool.js是一款专为前端开发者设计的免费、轻量级的JavaScript工具库。它旨在简化常见的编程任务,提高开发效率,同时保持代码的简洁性和可维护性。xTool.js库不仅小巧,...

    javascript文档

    这篇详尽的文档“javascript文档”深入探讨了JavaScript的各个方面,包括基础语法、对象、函数、DOM操作以及高级特性,旨在帮助读者熟练掌握这门语言。 一、基础语法 JavaScript的基础语法包括变量声明(var、let、...

Global site tag (gtag.js) - Google Analytics