我想測試大型調用堆棧。具體來說,當調用堆棧長度達到1000時,我想要一個控制檯警告。這通常意味着我做了一些愚蠢的事情,並可能導致微妙的錯誤。在JavaScript中調用堆棧大小
我可以在JavaScript中計算調用堆棧的長度嗎?
我想測試大型調用堆棧。具體來說,當調用堆棧長度達到1000時,我想要一個控制檯警告。這通常意味着我做了一些愚蠢的事情,並可能導致微妙的錯誤。在JavaScript中調用堆棧大小
我可以在JavaScript中計算調用堆棧的長度嗎?
這是一個可以在所有主流瀏覽器中工作的功能,雖然它不能在ECMAScript 5嚴格模式下工作,因爲arguments.callee
和caller
已經在嚴格模式下被刪除。
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
UPDATE 2011
在ES5嚴格模式11月1日,根本就no way to navigate the call stack。剩下的唯一選擇是解析由new Error().stack
返回的字符串,這是非標準的,沒有普遍支持並且顯然有問題,甚至是這個may not be possible for ever。
UPDATE 2013年8月13日
這種方法也由以下事實:即在一個調用棧調用一次以上(例如,經由遞歸)函數將拋出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());
一種不同的方法是測量可用然後通過觀察可用空間的多少來確定堆棧上的已用空間。在代碼:
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)確定所述用完堆棧空間是一個潛在的一個昂貴的操作當虛擬機具有較大的堆棧大小時,報告的數字不一定是遞歸的數量,而是測量實際使用的空間在堆棧上(當然,這也是一個優點)。我已經看到了自動生成的代碼,其中包含的函數使用上面的stackSizeExplorer
函數的2000次遞歸在堆棧上使用相同的空間。
注意:我只用node.js測試了上面的代碼。但我認爲它可以適用於所有使用靜態堆棧大小的虛擬機。
[this](http://eriwen.com/javascript/js-stack-trace/)有幫助嗎? –
代碼Dave Newton指出拋出一個異常,將其捕獲爲'e'並基於瀏覽器檢查其屬性。對於Chrome和Mozilla,它使用'e.stack',對於Opera 10+,它使用'e.stacktrace';對於其他人,它會嘗試理解'e.message'屬性。 – 2011-09-18 15:25:09
錯誤堆棧跟蹤只能放棄10個堆棧條目嗎? http://jsfiddle.net/pimvdb/AuyP7/ – pimvdb