在JavaScript中调用堆栈大小

我想testing大的调用堆栈。 具体来说,当调用堆栈长度达到1000时,我想要一个控制台警告。这通常意味着我做了一些愚蠢的事情,并可能导致微妙的错误。

我可以在JavaScript中计算调用堆栈的长度吗?

这里有一个函数可以在所有主stream浏览器中使用,但是在ECMAScript 5严格模式下不能使用,因为arguments.calleecaller已经被严格的删除了。

 function getCallStackSize() { var count = 0, fn = arguments.callee; while ( (fn = fn.caller) ) { count++; } return count; } 

例:

 function f() { g(); } function g() { h(); } function h() { alert(getCallStackSize()); } f(); // Alerts 3 

更新2011年11月1日

在ES5严格模式下,根本无法导航调用堆栈 。 剩下的唯一select是parsing由new Error().stack返回的string,这是非标准的,不是普遍支持的,显然是有问题的,甚至可能永远不可能 。

2013年8月13日更新

这个方法也受限于这样一个事实,即在一个调用堆栈中多次调用的函数(例如,通过recursion)会将getCallStackSize()引发到一个无限循环(正如@Randomblue在注释中指出的那样)。 getCallStackSize()一个改进版本如下:它跟踪之前看到的函数,以避免陷入无限循环。 但是,返回值是调用堆栈中不同函数对象在遇到重复之前的数量,而不是完整调用堆栈的真实大小。 不幸的是,这是最好的。

 var arrayContains = Array.prototype.indexOf ? function(arr, val) { return arr.indexOf(val) > -1; } : function(arr, val) { for (var i = 0, len = arr.length; i < len; ++i) { if (arr[i] === val) { return true; } } return false; }; function getCallStackSize() { var count = 0, fn = arguments.callee, functionsSeen = [fn]; while ( (fn = fn.caller) && !arrayContains(functionsSeen, fn) ) { functionsSeen.push(fn); count++; } return count; } 

你可以使用这个模块: https : //github.com/stacktracejs/stacktrace.js

调用printStackTrace返回数组中的堆栈跟踪,然后可以检查它的长度:

 var trace = printStackTrace(); console.log(trace.length()); 

另一种方法是测量顶层堆栈帧中堆栈的可用大小,然后通过观察可用空间的多less来确定堆栈上的已用空间。 在代码中:

 function getRemainingStackSize() { var i = 0; function stackSizeExplorer() { i++; stackSizeExplorer(); } try { stackSizeExplorer(); } catch (e) { return i; } } var baselineRemStackSize = getRemainingStackSize(); var largestSeenStackSize = 0; function getStackSize() { var sz = baselineRemStackSize - getRemainingStackSize(); if (largestSeenStackSize < sz) largestSeenStackSize = sz; return sz; } 

例如:

 function ackermann(m, n) { if (m == 0) { console.log("Stack Size: " + getStackSize()); return n + 1; } if (n == 0) return ackermann(m - 1, 1); return ackermann(m - 1, ackermann(m, n-1)); } function main() { var m, n; for (var m = 0; m < 4; m++) for (var n = 0; n < 5; n++) console.log("A(" + m + ", " + n + ") = " + ackermann(m, n)); console.log("Deepest recursion: " + largestSeenStackSize + " (" + (baselineRemStackSize-largestSeenStackSize) + " left)"); } main(); 

这种方法当然有两个主要缺点:

(1)当VM具有较大的堆栈大小时,确定用完的堆栈空间是潜在的昂贵的操作

(2)报告的数字不一定是recursion的数量,而是堆栈中使用的实际空间的度量(当然,这也是一个优点)。 我已经看到了自动生成的代码,其中包含的函数在堆栈中使用相同的空间作为上面的stackSizeExplorer函数的2000次recursion。

注意:我只用node.jstesting了上面的代码。 但是我认为它可以和所有使用静态堆栈大小的虚拟机一起工作。