关于堆栈的溢出问题,在Javascript日常开发中很常见,Google了下,相关问题还是比较多的。本文旨在描述如何解决此类问题。 首先看一个实例(当然你可以使用更容易的方式实现,这里我们仅探讨递归):

function isEven (num) {
if (num === 0) {
return true;
} if (num === 1) {
return false;
} return isEven(Math.abs(num) - 2);
} //Outputs: true
console.log(isEven(10)); //Outputs: false
console.log(isEven(9));

当我们把参数改成10000时,运行下例会发生堆栈溢出:

function isEven (num) {
if (num === 0) {
return true;
} if (num === 1) {
return false;
} return isEven(Math.abs(num) - 2);
} //不同的javascript引擎报错可能不同
//Outputs: Uncaught RangeError: Maximum call stack size exceeded
console.log(isEven(10000));

原因是每次执行代码时,都会分配一定尺寸的栈空间(Windows系统中为1M),每次方法调用时都会在栈里储存一定信息(如参数、局部变量、返回值等等),这些信息再少也会占用一定空间,成千上万个此类空间累积起来,自然就超过线程的栈空间了。那么如何解决此类问题?

使用闭包:

function isEven (num) {
if (num === 0) {
return true;
} if (num === 1) {
return false;
} return function() {
return isEven(Math.abs(num) - 2);
}
}
//Outputs: true
console.log(isEven(4)()());

此时每次调用时,返回一个匿名函数,匿名函数执行相关的参数和局部变量将会释放,不会额外增加堆栈大小。

优化调用:

上例调用比较麻烦,优化如下:

function isEven (num) {
if (num === 0) {
return true;
} if (num === 1) {
return false;
} return function() {
return isEven(Math.abs(num) - 2);
}
} function trampoline (func, arg) {
var value = func(arg); while(typeof value === "function") {
value = value();
} return value;
}
//Outputs: true
console.log(trampoline(isEven, 10000)); //Outputs: false
console.log(trampoline(isEven, 10001));

现在我们可以解决堆栈溢出问题了,但是不是感觉每次tarmpoline(isEven, 1000)这种调用方式不是很好,我们可以使用bind来绑定:

function isEven(n) {
/**
* [isEvenInner 递归]
* @param {[type]} num [description]
* @return {Boolean} [description]
*/
function isEvenInner (n) {
if (n === 0) {
return true;
} if (n === 1) {
return false;
} return function() {
return isEvenInner(Math.abs(n) - 2);
}
}
/**
* [trampoline 迭代]
* @param {[type]} func [description]
* @param {[type]} arg [description]
* @return {[type]} [description]
*/
function trampoline (func, arg) {
var value = func(arg); while(typeof value === "function") {
value = value();
} return value;
} return trampoline.bind(null, isEvenInner)(n);
}
//Outputs: true
console.log(isEven(10000)); //Outputs: false
console.log(isEven(10001));

虽然上例实现了我们想要的效果,但是trampoline函数还是有一定的局限性:

1.假设你只传递一个参数给递归函数

value = func(arg); 修改为 value = func.apply(func, arg);

2.假设最后的返回值不是一个函数 关于更健壮性的实现,请看underscore-contrib中源码。

感谢您的阅读,文中不妥之处还望批评指正,文章已同步至个人博客如果你有好的建议,欢迎留言,么么哒!

转载声明:

本文标题:Javascript中递归造成的堆栈溢出及解决方案

本文链接:http://www.zuojj.com/archives/1115.html,转载请注明转自Benjamin-专注前端开发和用户体验

Javascript中递归造成的堆栈溢出及解决方案的更多相关文章

  1. 使用es6的蹦床函数解决递归造成的堆栈溢出

      首先,我们先定义一个函数,使用递归的思想写求和的方法: function sum(x, y) { if (y > 0) { return sum(x + 1, y - 1); } else ...

  2. arcgis engine 中出现的内存堆栈溢出问题。

    两种解决方案: 1.循环加载mxd文档的时候出现的堆栈溢出,解决办法是每次循环结束时清空FeatureLayer,感觉并不好,但是确实可以实现功能. 2.循环调取featureclass的search ...

  3. javascript递归导致的堆栈溢出

    function foo() {foo(); //setTimeout(foo, 0);   } foo() 原因是每次执行代码时,都会分配一定尺寸的栈空间(Windows系统中为1M),每次方法调用 ...

  4. python递归次数和堆栈溢出问题

    在做递归的时候,测试了一下python的递归能力. 如果不设置递归次数的话,大概只能在992次左右,就会出现错误:RuntimeError: maximum recursion depth excee ...

  5. Javascript中递归的调用

    递归函数就是调用自身,如下所示: function factorial(num){ if(num<=1){ return 1; }else{ return num*factorial(num-1 ...

  6. JPA 一对多双向映射 结果对象相互迭代 造成堆栈溢出问题方法

    问题: JPA 在双向映射时,会相互包含对方的实例,相互引用,造成递归迭代,堆栈溢出(java.lang.StackOverflowError). 分析: 在后端向前端传递的时候会将数据序列化,转为j ...

  7. JavaScript中的递归

    译者按: 程序员应该知道递归,但是你真的知道是怎么回事么? 原文: All About Recursion, PTC, TCO and STC in JavaScript 译者: Fundebug 为 ...

  8. javascript中的堆栈、深拷贝和浅拷贝、闭包

    堆栈 在javascript中,堆内存是用来存放引用类型的空间环境 而栈内存,是存储基本类型和指定代码的环境 在对象中的属性名具有唯一性,数字属性名=字符串属性名,但是在测试的时候你会发现,好像所有属 ...

  9. javascript中的Array对象 —— 数组的合并、转换、迭代、排序、堆栈

    Array 是javascript中经常用到的数据类型.javascript 的数组其他语言中数组的最大的区别是其每个数组项都可以保存任何类型的数据.本文主要讨论javascript中数组的声明.转换 ...

随机推荐

  1. hdu 2896 字典树解法

    #include <iostream> #include <cstring> #include <cstdio> #include <cstdlib> ...

  2. js中this对象,call,apply

  3. 在线学习体验大PK 云智慧发布在线教育网站性能监测报告

    互联网不但改变了我们的生活.娱乐和消费方式,也推动各行各业进行着快速变革,越来越多的职场人士必须通过不断的学习.充电才能跟上行业发展的步伐,获得职业的提升,而这也引发了国内教育市场的爆炸式发展.据统计 ...

  4. 【MVC】 异常处理

    [MVC] 异常处理 一 . 自定义 HandleErrorAttribute public class ExceptionLogAttribute : HandleErrorAttribute { ...

  5. JavaScript小功能

    1. JS判断是否为一个有效日期 1 2 3 4 function check(date){     return (new Date(date).getDate()==date.substring( ...

  6. java核心知识点学习----重点学习线程池ThreadPool

    线程池是多线程学习中需要重点掌握的. 系统启动一个新线程的成本是比较高的,因为它涉及与操作系统交互.在这种情形下,使用线程池可以很好的提高性能,尤其是当程序中需要创建大量生存期很短暂的线程时,更应该考 ...

  7. linux学习之——学习路线(摘抄)

    摘抄某笔者的Linux练习的道路图(rolistingmap): 对比一下为什么要学习linux 了解Linux的基础常识,这些包括了用户管理.群组的概念.权限的观念等 掌握至多50个以上的常用命令 ...

  8. 单片机TM4C123学习(九):PWM

    1.头文件与变量定义 #include "tiva_pwm.h" // PWM 2.初始化 // PWM 初始化,频率为1000,占空比为0 M1PWM7_init(, ); // ...

  9. mysql学习(2)-MySQL服务器优化

    调优思路: 1.数据库设计与规划--以后再修该很麻烦,估计数据量,使用什么存储引擎 2.数据的应用--怎样取数据,sql语句的优化 3.mysql服务优化--内存的使用,磁盘的使用 4.操作系统的优化 ...

  10. poj练习题的方法

    poj1010--邮票问题 DFSpoj1011--Sticks dfs + 剪枝poj1020--拼蛋糕poj1054--The Troublesome Frogpoj1062--昂贵的聘礼poj1 ...