递归是拖慢脚本运行速度的大敌之一,太多的递归会让浏览器变得越来越慢直到死掉或者莫名其妙的突然自动退出。这里我们可以通过memoization技术来替代函数中太多的递归调用,提升JavaScript效率。

递归是拖慢脚本运行速度的大敌之一。太多的递归会让浏览器变得越来越慢直到死掉或者莫名其妙的突然自动退出,所以我们一定要解决在JavaScript中出现的这一系列性能问题。

我们可以通过memoization技术来替代函数中太多的递归调用。memoization是一种可以缓存之前运算结果的技术,这样我们就不需要重新计算那些已经计算过的结果。

对于通过递归来进行计算的函数,memoization简直是太有用了。我现在使用的memoizer是由Crockford写的,主要应用在那些返回整数的递归运算中。当然并不是所有的递归函数都返回整数,所以我们需要一个更加通用的memoizer()函数来处理更多类型的递归函数。

  1. function memoizer(fundamental, cache) {
  2. cachecache = 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;
  10. }

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

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

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

有的观点认为,任何使用递归的情况,如果有需要,都可以使用迭代来代替。实际上,递归和迭代经常会被作为互相弥补的方法,尤其是在另外一种 出问题的情况下。将递归算法转换为迭代算法的技术,也是和开发语言无关的。这对JavaScript来说是很重要的,因为很多东西在执行环境中是受到限制的。让我们回顾一个典型的递归算法,比如说归并排序,在JavaScript中实现这个算法需要下面的代码:

  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. function mergeSort(items) {
  14. if (items.length == 1) {
  15. return items;
  16. }
  17. var middle = Math.floor(items.length / 2),
  18. left = items.slice(0, middle),
  19. right = items.slice(middle);
  20. return merge(mergeSort(left), mergeSort(right));
  21. }

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

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

这个归并排序算法实现使用了一系列循环来代替递归进行排序。由于归并排序首先要将数组拆分成若干只有一个元素的数组,这个方法更加明确的执行了这个操作,而不是通过递归函数隐晦的完成。work数组被初始化为包含一堆只有一个元素数组的数组。

在循环中每次会合并两个数组,并将合并后的结果放回work数组中。当函数执行完成后,排序的结果会通过work数组中的第一个元素返回。在这个归并排序的实现中,没有使用任何递归,同样也实现了这个算法。

提升JavaScript递归效率:Memoization技术详解[转载]的更多相关文章

  1. NAT(地址转换技术)详解(转载)

    作者:逃离地球的小小呆 来源:CSDN 原文:https://blog.csdn.net/gui951753/article/details/79593307版权声明:本文为博主原创文章,转载请附上博 ...

  2. CDN学习笔记二(技术详解)

    一本好的入门书是带你进入陌生领域的明灯,<CDN技术详解>绝对是带你进入CDN行业的那盏最亮的明灯.因此,虽然只是纯粹的重点抄录,我也要把<CDN技术详解>的精华放上网.公诸同 ...

  3. CDN技术详解及实现原理

    CDN技术详解 一本好的入门书是带你进入陌生领域的明灯,<CDN技术详解>绝对是带你进入CDN行业的那盏最亮的明灯.因此,虽然只是纯粹的重点抄录,我也要把<CDN技术详解>的精 ...

  4. hadoop大数据基础框架技术详解

    一.什么是大数据 进入本世纪以来,尤其是2010年之后,随着互联网特别是移动互联网的发展,数据的增长呈爆炸趋势,已经很难估计全世界的电子设备中存储的数据到底有多少,描述数据系统的数据量的计量单位从MB ...

  5. [转帖]技术盛宴 | 关于PoE以太网供电技术详解

    技术盛宴 | 关于PoE以太网供电技术详解 https://smb.pconline.com.cn/1208/12085824.html   [PConline 干货铺]随着物联网技术飞速发展,需要提 ...

  6. 高性能JavaScript模板引擎实现原理详解

    这篇文章主要介绍了JavaScript模板引擎实现原理详解,本文着重讲解artTemplate模板的实现原理,它采用预编译方式让性能有了质的飞跃,是其它知名模板引擎的25.32 倍,需要的朋友可以参考 ...

  7. javascript常用经典算法实例详解

    javascript常用经典算法实例详解 这篇文章主要介绍了javascript常用算法,结合实例形式较为详细的分析总结了JavaScript中常见的各种排序算法以及堆.栈.链表等数据结构的相关实现与 ...

  8. 架构设计:远程调用服务架构设计及zookeeper技术详解(下篇)

    一.下篇开头的废话 终于开写下篇了,这也是我写远程调用框架的第三篇文章,前两篇都被博客园作为[编辑推荐]的文章,很兴奋哦,嘿嘿~~~~,本人是个很臭美的人,一定得要截图为证: 今天是2014年的第一天 ...

  9. 《CDN技术详解》 - CDN知多少?

    开发时间久了,就会接触到性能和并发方面的问题,如果说,在自己还是菜鸟的时候完全不用理会这种问题或者说有其他的高手去处理这类问题,那么,随着经验的丰富起来,自己必须要独立去处理了.或者,知道思路也行,毕 ...

随机推荐

  1. hdu 3667(最小费用最大流+拆边)

    Transportation Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)To ...

  2. 微信小程序-ios系统-下拉上拉出现白色,如何处理呢?

    这几天做小程序,有些页面都是全屏的背景,在安卓上背景是固定的,而在ios上上拉下拉出现白色,测试说体验不太好,一开始我以为是下拉上拉刷新造成的,关闭了依然是这样.为了体验好点,可以按一下解决: 方式一 ...

  3. nutch 抓取需要登录的网页

    题记:一步一坑,且行且珍惜 最近接到任务,要利用nutch去抓取公司内部系统的文章,可是需要登录才能抓到.对于一个做.net,不熟悉java,不知道hadoop,很少接触linux的我,这个过程真是艰 ...

  4. Openstack 清除openstack网络与路由 (十七)

    一)清除openstack网络与路由 “清除openstack网络与路由”和”添加openstack网络与路由”的操作步骤相反. 添加网络或路由时是先建 搭建网络>搭建子网>建立端口, 而 ...

  5. python访问web的利器:urllib2

    使用Python访问网页主要有三种方式: urllib, urllib2, httpliburllib比较简单,功能相对也比较弱,httplib简单强大,但好像不支持session1. 最简单的页面访 ...

  6. Codeforces 1131 B. Draw!-暴力 (Codeforces Round #541 (Div. 2))

    B. Draw! time limit per test 2 seconds memory limit per test 256 megabytes input standard input outp ...

  7. zookeeper,hadoop安装部署其实与防火墙无关

    网上查看了很多人关于hadoop,zookeeper的文章,大多都把关闭防火墙作为首要前提,个人觉得这大可不必. 首先你需要知道你部署的是什么东西,它需要哪些端口即可.把相关端口打开就可以了啊.然后把 ...

  8. 洛谷——P1748 H数

    P1748 H数 题目背景 无 题目描述 所谓H数,是指只含有2,3,5,7这些质因数的数,如630是H数,而22不是.现在要求输出第n个H数,为了方便起见将H[1]定为1.已知n不超过10000,最 ...

  9. Xamarin开发安装Visual Studio 2015 update2报错的解决办法

    Xamarin开发安装Visual Studio 2015 update2报错的解决办法错误信息:update 2 requires a member of the visual studio 201 ...

  10. JDBC 编程初步

    JDBC 概述 什么是JDBC 是一种用于执行SQL语句的Java API,它由一组用Java语言编写的类和接口组成,JDBC提供了一种操作数据的标准,JDBC的目标是使Java程序员使用JDBC可以 ...