`
mtnt2008
  • 浏览: 372271 次
  • 性别: Icon_minigender_1
  • 来自: 北京
文章分类
社区版块
存档分类
最新评论

Speed up your JavaScrip 3[转]

阅读更多

影响JavaScript性能的另外一个杀手就是递归,在上一节中提到采用memoization技术可以优化计算数值的递归函数,但 memoization不是万能的,不是所有的递归函数都可以用memoization技术优化,本文介绍了这些情况,并介绍了解决办法,就是将递归转换 为迭代,同时需要注意,本文末尾介绍的方案不是最终的方案,还需要和上一节优化循环的方案综合起来才能达到最佳效果。

【原文】Speed up your JavaScript, Part 3
【作者】Nicholas C. Zakas
【译文】http://cuimingda.com/2009/02/speed-up-your-javascript-part-3.html
【译者】明达

以下是对原文的翻译

递归是拖慢脚本运行速度的大敌之一。太多的递归会让浏览器变得越来越慢直到死掉或者莫名其妙的突然自动退出,所以我们一定要解决在 JavaScript中出现的这一系列性能问题。在这个系列文章的第二篇中,我曾经简短的介绍了如何通过memoization技术来替代函数中太多的递 归调用。memoization是一种可以缓存之前运算结果的技术,这样我们就不需要重新计算那些已经计算过的结果。对于通过递归来进行计算的函 数,memoization简直是太有用了。我现在使用的memoizer是由 Crockford写的,主要应用在那些返回整数的递归运算中。当然并不是所有的递归函数都返回整数,所以我们需要一个更加通用的memoizer()函 数来处理更多类型的递归函数。

 

Java代码
  1. function memoizer(fundamental, cache) {  
  2.   cache = cache || {};  
  3.   var shell = function(arg) {  
  4.       if  (! (arg in cache)) {  
  5.           cache[arg] = fundamental(shell, arg);  
  6.       }  
  7.       return  cache[arg];  
  8.   };  
  9.   return  shell;}  
function memoizer(fundamental, cache) {
  cache = cache || {};
  var shell = function(arg) {
      if (! (arg in cache)) {
          cache[arg] = fundamental(shell, arg);
      }
      return cache[arg];
  };
  return shell;}

 

这个版本的函数和Crockford写的版本有一点点不同。首先,参数的顺序被颠倒了,原有函数被设置为第一个参数,第二个参数是缓存对象,为可选 参数,因为并不是所有的递归函数都包含初始信息。在函数内部,我将缓存对象的类型从数组转换为对象,这样这个版本就可以适应那些不是返回整数的递归函数。 在shell函数里,我使用了in操作符来判断参数是否已经包含在缓存里。这种写法比测试类型不是undefined更加安全,因为undefined是 一个有效的返回值。我们还是用之前提到的斐波纳契数列来做说明:

Java代码
  1. var fibonacci = memoizer(function(recur, n) {  
  2.   return  recur(n -  1 ) + recur(n -  2 );  
  3. }, { "0" 0 "1" 1 } );  
var fibonacci = memoizer(function(recur, n) {
  return recur(n - 1) + recur(n - 2);
}, { "0": 0, "1": 1} );

 

 

同样的,执行fibonacci(40)这个函数,只会对原有的函数调用40次,而不是夸张的331,160,280次。memoization对 于那些有着严格定义的结果集的递归算法来说,简直是棒极了。然而,确实还有很多递归算法不适合使用memoization方法来进行优化。

我在学校时的一位教授一直坚持认为,任何使用递归的情况,如果有需要,都可以使用迭代来代替。实际上,递归和迭代经常会被作为互相弥补的方法,尤其 是在另外一种出问题的情况下。将递归算法转换为迭代算法的技术,也是和开发语言无关的。这对JavaScript来说是很重要的,因为很多东西在执行环境 中是受到限制的(the importance in JavaScript is greater, though, because the resources of the execution environment are so restrictive.)。让我们回顾一个典型的递归算法,比如说归并排序,在JavaScript中实现这个算法需要下面的代码:

 

Java代码
  1. function merge(left, right) {  
  2.   var result = [];  
  3.   while  (left.length >  0  && right.length >  0 ) {  
  4.       if  (left[ 0 ] < right[ 0 ]) {  
  5.           result.push(left.shift());  
  6.       } else  {  
  7.           result.push(right.shift());  
  8.       }  
  9.   }  
  10.   return  result.concat(left).concat(right);  
  11. }  
  12.   
  13. //采用递归实现的归并排序算法   
  14. function mergeSort(items) {  
  15.   if  (items.length ==  1 ) {  
  16.       return  items;  
  17.   }  
  18.   var middle = Math.floor(items.length / 2 ),  
  19.   left = items.slice(0 , middle),  
  20.   right = items.slice(middle);  
  21.   return  merge(mergeSort(left), mergeSort(right));  
  22. }  
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));
}

 

调用mergeSort()函数处理一个数组,就可以返回经过排序的数组。注意每次调用mergeSort()函数,都会有两次递归调用。这个算法 不可以使用memoization来进行优化,因为每个结果都只计算并使用一次,就算缓冲了结果也没有什么用。如果你使用mergeSort()函数来处 理一个包含100个元素的数组,总共会有199次调用。1000个元素的数组将会执行1999次调用。在这种情况下,我们的解决方案是将递归算法转换为迭 代算法,也就是说要引入一些循环(关于算法,可以参考这篇《List Processing: Sort Again, Naturally 》):

 

Java代码
  1. // 采用迭代实现的归并排序算法   
  2. function mergeSort(items) {  
  3.   if  (items.length ==  1 ) {  
  4.       return  items;  
  5.   }  
  6.   var work = [];  
  7.   for  (var i =  0 ,  
  8.   len = items.length; i < len; i++) {  
  9.       work.push([items[i]]);  
  10.   }  
  11.   work.push([]); //in case of odd number of items   
  12.   for  (var lim = len; lim >  1 ; lim = (lim +  1 ) /  2 ) {  
  13.       for  (var j =  0 ,  
  14.       k = 0 ; k < lim; j++, k +=  2 ) {  
  15.           work[j] = merge(work[k], work[k + 1 ]);  
  16.       }  
  17.       work[j] = []; //in case of odd number of items   
  18.   }  
  19.   return  work[ 0 ];  
  20. }  
// 采用迭代实现的归并排序算法
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];
}

 

这个归并排序算法实现使用了一系列循环来代替递归进行排序。由于归并排序首先要将数组拆分成若干只有一个元素的数组,这个方法更加明确的执行了这个 操作,而不是通过递归函数隐晦的完成。work数组被初始化为包含一堆只有一个元素数组的数组。在循环中每次会合并两个数组,并将合并后的结果放回 work数组中。当函数执行完成后,排序的结果会通过work数组中的第一个元素返回。在这个归并排序的实现中,没有使用任何递归,同样也实现了这个算 法。然而,这样做却引入了大量的循环,循环的次数基于要排序的数组中元素的个数,所以我们可能需要使用在 上篇讨论过的技术 来进行修订,处理这些额外开销。

总结一下基本原则,不管是什么时候使用递归的时候都应该小心谨慎。memoization和迭代是代替递归的两种解决方案,最直接的结果当然就是避免那个 提示脚本失控的对话框

 

 

分享到:
评论

相关推荐

    Beginning JavaScript, 4th Edition

    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 ...

    Mastering.JavaScript.High.Performance.1784397296

    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 ...

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

    Zakas 于 2009 年 1 月 20 日在个人网站上发表的《Speed up your JavaScript, Part 2》。本文主要讨论了如何重构嵌套循环、递归,以及那些在函数内部同时执行很多子操作的函数,以提高 JavaScript 的运行速度。 ...

    [JavaScript] JavaScript 从入门到精通 (英文版)

    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...

    Beginning JavaScript with DOM Scripting and Ajax: Second Editon

    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 ...

    Beginning JavaScript with DOM Scripting and Ajax (pdf + ePub)

    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 ...

    JavaScript 圣经第5版-Javascript编程宝典--黄金版 .rar

    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 ...

    Web性能优化系列 10个提升JavaScript性能的技巧

    当谈到 JS 性能的时候,Zakas差不多就是你要找的,2010年六月他在Google Tech Talk发表了名为《Speed Up Your Javascript》的演讲。 但 Javascript 性能优化绝不是一种书面的技术,Nicholas 的技术演进列出了10条...

    JavaScript Programmers Reference

    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...

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

    原文标题:Speed up your JavaScript, Part 1 原文作者:Nicholas C. Zakas 在我 上一篇帖子 (译文 ) 中,谈到了各个浏览器究竟会在什么情况下弹出脚本失控提示,对于Internet Explorer 来说,当浏览器执行了数量...

    PhoneGap: Beginner’s Guide

    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 ...

    PhoneGap Beginners Guide Sep 2011

    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 ...

    ReactJS.Blueprints.1785886541

    Learn how to speed up your development process and save valuable time Work though step-by-step tutorials that provide easy-to-understand solutions to real-world problems Book Description The ...

    React.Native.Blueprints.epub

    Use external modules to speed up the development and maintenance of your projects Explore the different UI and code patterns to be used for iOS and Android Understand the best practices which can be ...

Global site tag (gtag.js) - Google Analytics