Javascript中递归造成的堆栈溢出及解决方案
关于堆栈的溢出问题,在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中递归造成的堆栈溢出及解决方案的更多相关文章
- 使用es6的蹦床函数解决递归造成的堆栈溢出
首先,我们先定义一个函数,使用递归的思想写求和的方法: function sum(x, y) { if (y > 0) { return sum(x + 1, y - 1); } else ...
- arcgis engine 中出现的内存堆栈溢出问题。
两种解决方案: 1.循环加载mxd文档的时候出现的堆栈溢出,解决办法是每次循环结束时清空FeatureLayer,感觉并不好,但是确实可以实现功能. 2.循环调取featureclass的search ...
- javascript递归导致的堆栈溢出
function foo() {foo(); //setTimeout(foo, 0); } foo() 原因是每次执行代码时,都会分配一定尺寸的栈空间(Windows系统中为1M),每次方法调用 ...
- python递归次数和堆栈溢出问题
在做递归的时候,测试了一下python的递归能力. 如果不设置递归次数的话,大概只能在992次左右,就会出现错误:RuntimeError: maximum recursion depth excee ...
- Javascript中递归的调用
递归函数就是调用自身,如下所示: function factorial(num){ if(num<=1){ return 1; }else{ return num*factorial(num-1 ...
- JPA 一对多双向映射 结果对象相互迭代 造成堆栈溢出问题方法
问题: JPA 在双向映射时,会相互包含对方的实例,相互引用,造成递归迭代,堆栈溢出(java.lang.StackOverflowError). 分析: 在后端向前端传递的时候会将数据序列化,转为j ...
- JavaScript中的递归
译者按: 程序员应该知道递归,但是你真的知道是怎么回事么? 原文: All About Recursion, PTC, TCO and STC in JavaScript 译者: Fundebug 为 ...
- javascript中的堆栈、深拷贝和浅拷贝、闭包
堆栈 在javascript中,堆内存是用来存放引用类型的空间环境 而栈内存,是存储基本类型和指定代码的环境 在对象中的属性名具有唯一性,数字属性名=字符串属性名,但是在测试的时候你会发现,好像所有属 ...
- javascript中的Array对象 —— 数组的合并、转换、迭代、排序、堆栈
Array 是javascript中经常用到的数据类型.javascript 的数组其他语言中数组的最大的区别是其每个数组项都可以保存任何类型的数据.本文主要讨论javascript中数组的声明.转换 ...
随机推荐
- Lambda表达式的语法格式
Lambda表达式的语法格式: 参数列表 => 语句或语句块 “Lambda表达式”是委托的实现方法,所以必须遵循以下规则: 1)“Lambda表达式”的参数数量必须和“委托”的参数数量相同: ...
- usb host驱动
参考 http://blog.csdn.net/star530/article/details/8019604
- python学习笔记2-functools.wraps 装饰器
wraps其实没有实际的大用处, 就是用来解决装饰器导致的原函数名指向的函数 的属性发生变化的问题: 装饰器装饰过函数func, 此时func不是指向真正的func,而是指向装饰器中的装饰过的函数 i ...
- 2015年毕业生收到的offer和薪资透露
个人情况:全国排名70多的大学,非211,计算机专业硕士 收到的offer: 苏州同程旅游--6k/月 ,自己要了这么多,就给了这么多,没有规范.苏州蜗牛科技--8k/月 ,给了个口头offer,要去 ...
- java基础(1)-比较jdk5,jdk6,jdk7的新特性
jdk8已经出来好长时间了,这里自己学习时简单总结的jdk5,jdk6和jdk7的新特性:本文提纲: 一.jdk5的新特性 二.jdk6的新特性 三.jdk7的新特性 一.jdk5的新特性 首先简单介 ...
- 常用UML模型简要小结
关系: 关联(组合,生命周期相同:聚合,物以类聚),依赖,泛化(继承),实现 还有 包含,细化复用已有用例:扩展,非必要主要的用例 图: 1.用例图:就是描述一个功能场景(集合),其实用例编写(前后置 ...
- 杀死你网站SEO的5个技术
胡亮亮先生(网迈SEO总监)在其微信公众帐号里发布了文章<杀死你网站SEO的5个技术>,发出来给大家分享一下: 应百度站长平台邀请,抽空把这篇文章做一些细节上的补充 ,欢迎大家关注并讨论. ...
- python 生产者消费者模型
import time def consumer(name): print("%s开始吃包子了"%name) while True: ret = yield time.sleep( ...
- POJ1222_EXTENDED LIGHTS OUT
给出5*6的位置,每个位置有一个灯,一开始每个灯有各自的状态,你可以选定一些位置使得所有与这个位置相邻以及位置本身的灯都取反. 输出合法方案. 本来是找高斯消元找到这个题目的,可是....我发现可以直 ...
- BST、B树、B+树、B*树
一. BST BST即二叉搜索树Binary Search Tree(又叫二叉排序树Binary Sort Tree).它有以下特点: 所有非叶子结点至多拥有两个儿子(Left和Right): 所有结 ...