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中数组的声明.转换 ...
随机推荐
- BootLoader 详解(3)
BootLoader的stage2 stage2的代码是C语言来实现的,以便于实现更复杂的功能和取得更好的代码可读性和移植性.它与普通C语言程序不同的是,在编译和链接BootLoader这样的程序时, ...
- Servlet 添加购物车
import java.io.IOException;import java.io.PrintWriter;import java.util.ArrayList;import java.util.It ...
- lucene query
在lucene的搜索中,最重要的无疑就是对query的理解和掌握了.这里扒拉一下源码(版本3.5.0)的query和query实现: query是一个抽象类,实现类有以下几个: termQuery m ...
- iOS /* */多个嵌套解决
解决办法 #if 0 /* */ #endif
- Calling startActivity() from outside of an Activity context requires the FLAG_ACTIVITY_NEW_TASK fla
转载于 http://blog.csdn.net/wike163/article/details/6678073 从一个Activity中要通过intent调出另一个Activity的话,需 ...
- LeetCode OJ 150. Evaluate Reverse Polish Notation
Evaluate the value of an arithmetic expression in Reverse Polish Notation. Valid operators are +, -, ...
- Hibernate 根据实体名称得到DB表名以及表对应的Sequence name
DB: oracle 10g; entityName:com.signaldemand.flank.hibernate.model.实体名 1. 根据实体名获取DB表相对应的表名 Class<? ...
- 通过字符串寻找与字符串一致的model的属性
// 取得选中权限集合 string[] arrAuthorityId = this.hidAuthorityIds.Value.TrimEnd(',').Split(','); BLBQ_Autho ...
- javascript this
最近看了很多人的微博,主要是“追梦子”的微博,总结了一下.希望大家多多指点. 1. 没有new this的指向问题 this的指向在函数创建的时候是决定不了的,在调用的时候才能决定,谁调用的就指向 ...
- nginx 报错 upstream timed out (110: Connection timed out)解决方案
nginx 作PHP的web接口服务器. 在线上发现时不时经常崩溃.504,导致接口访问无响应回复. 查看日志: [error] 11618#0: *324911 upstream timed out ...