1. 程式人生 > >JS的函式呼叫棧有多深?

JS的函式呼叫棧有多深?

譯者按: 有時候會遇到Maximum call stack size exceeded的問題,本文教你stack size的計算方法。

為了保證可讀性,本文采用意譯而非直譯。另外,本文版權歸原作者所有,翻譯僅用於學習。

如果你寫了一個一直呼叫自身的死迴圈,那麼恭喜你,很快就可以看到報錯:Uncaught RangeError: Maximum call stack size exceeded。那麼這個call stack size有多少呢?

1. 計算方法

如下的方法可以為你計算出你使用的JavaScript引擎可以支援多深的呼叫(由Ben Alman的一段程式碼獲得靈感):

function
computeMaxCallStackSize() {
try { return 1 + computeMaxCallStackSize(); } catch (e) { // Call stack overflow return 1; } }

執行得到如下三個結果:
- Node.js: 11034
- Firefox: 50994
- Chrome: 10402

這些數字代表了什麼呢?Mr.Aleph告訴我在V8,可呼叫的層數基於兩個方面:1. 棧的大小;2. 每一棧幀的大小(用於記錄函式引數和區域性變數)。你可以在computeMaxCallStackSize

宣告區域性變數來測試,你會發現數字變小。

2. ECMAScript 6中尾遞迴優化

ECMAScript 6支援尾遞迴優化:如果一個函式的最後一個操作是函式呼叫,那麼將會用“跳轉”而不是“子呼叫”。也就是說如果你將computeMaxCallStackSize重寫成如下形式,在ES6的嚴格模式下,就會一直運行了。

 function computeMaxCallStackSize(size) {
        size = size || 1;
        return computeMaxCallStackSize(size + 1);
    }

3. 亮點評論

  • Andrei: “ECMAScript 6”版本的程式碼根本跑不通。雖然size會被更改,但是最終並沒有值返回。
  • 回覆Andrei: 有趣!你不能用這段程式碼去計算stack size。在ES6下,這段程式碼會一直執行,因此不會返回資料。在其它情況下,會返回RangeError。為了使其工作,我把程式碼重寫了一下:
var computeMaxCallStackSize = (function() {
  return function() {
    var size = 0;
    function cs() {
      try {
        size++;
        return cs();
      } catch(e) {
        return size + 1;
      }
    }
    return cs();
  };
}());

關於Fundebug

Fundebug專注於JavaScript、微信小程式、小遊戲BUG監控,自從2016年雙十一正式上線,Fundebug已經服務了一年半時間,累計處理了5億+錯誤事件,得到了眾多知名使用者的認可。Fundebug支援主流前端框架的bug監控,歡迎各位老鐵體驗!