关于堆栈的溢出问题,在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. 循序渐进之Spring AOP(5) - 创建切面

    在掌握了可用的增强后,接下来要做的就是精确的描述切点.前面的示例都是指定一个目标类并把增强织入到所有方法中,实际开发显然会有更精细的筛选需求,比如对所有类中名称以test结尾的方法加入监控执行时间,或 ...

  2. super

    [super] Return a proxy object that delegates method calls to a parent or sibling class of type. This ...

  3. sqlserver巧用row_number和partition by分组取top数据

    SELECT * FROM( SELECT orderid,createtime, ROW_NUMBER() over(PARTITION by orderid order by createtime ...

  4. NDK相关以及同步相关博客收集

    http://www.cnblogs.com/heiing/archive/2013/01/20/2868268.htmlhttp://blog.sina.com.cn/s/blog_461c24d5 ...

  5. Centos7 mysql-community-5.7.11编译安装

    安装环境 [root@localhost ~]# cat /etc/centos-release CentOS Linux release 7.0.1406 (Core) 0x01 准备工作 1.到m ...

  6. AngularJs 返回上一页

    html <script src="lib/angular/angular-1.4.9/angular.js"></script> <script s ...

  7. jquery load 加载改造,只加载body

    背景: 项目中大量用到了jquery和easyui组件,原生load经常出现主页面异常,原因是组件被重复初始化.也考虑过用iframe,但是在实际项目中的效果,最终取消了iframe方案,也尝试了其他 ...

  8. LeetCode(84) Largest Rectangle in Histogram

    题目 Given n non-negative integers representing the histogram’s bar height where the width of each bar ...

  9. [Chapter 3 Process]Practice 3.4 Describe what happens when a context switch occurs if the new context is already loaded into one of the register sets.

    3.4 The Sun UltraSPARC processor has multiple register sets. Describe what happens when a context sw ...

  10. c# 身份证证号验证方法

    #region 验证身份证是否有效        /// <summary>        /// 验证身份证是否有效        /// </summary>        ...