InternalError: too much recursion

當函式呼叫過多,或函式缺少基礎情況時,會發生 JavaScript 的「too much recursion」或「Maximum call stack size exceeded」例外。

訊息

RangeError: Maximum call stack size exceeded (Chrome)
InternalError: too much recursion (Firefox)
RangeError: Maximum call stack size exceeded. (Safari)

錯誤類型

Firefox 中為 InternalError;Chrome 和 Safari 中為 RangeError

哪裡出錯了?

一個會呼叫自己的函式稱為遞迴函式。一旦滿足某個條件,函式就會停止呼叫自己。這稱為基礎情況

在某些方面,遞迴類似於迴圈。兩者都會多次執行相同的程式碼,且都需要一個條件(以避免無限迴圈,或在此情況下是無限遞迴)。當函式呼叫過多,或函式缺少基礎情況時,JavaScript 就會拋出此錯誤。

範例

此遞迴函式根據終止條件執行 10 次。

js
function loop(x) {
  if (x >= 10)
    // 「x >= 10」是終止條件
    return;
  // 做些事情
  loop(x + 1); // 遞迴呼叫
}
loop(0);

將此條件設定為一個極高的值,將無法運作:

js
function loop(x) {
  if (x >= 1000000000000) return;
  // 做些事情
  loop(x + 1);
}
loop(0);

// InternalError: too much recursion

這個遞迴函式缺少一個基礎情況。由於沒有終止條件,函式將會無限地呼叫自己。

js
function loop(x) {
  // 缺少基礎情況
  loop(x + 1); // 遞迴呼叫
}

loop(0);

// InternalError: too much recursion

Class 錯誤:遞迴過多

js
class Person {
  constructor() {}
  set name(name) {
    this.name = name; // 遞迴呼叫
  }
}

const tony = new Person();
tony.name = "Tonisha"; // InternalError: too much recursion

當一個值被指派給 name 屬性(this.name = name;)時,JavaScript 需要設定該屬性。當這種情況發生時,就會觸發 setter 函式。

在此範例中,當 setter 被觸發時,它被告知要再次做同樣的事情:設定它本應處理的同一個屬性。這會導致函式不斷地呼叫自己,從而造成無限遞迴。

如果在 getter 中使用相同的變數,也會出現此問題。

js
class Person {
  get name() {
    return this.name; // 遞迴呼叫
  }
}

為避免此問題,請確保在 setter 函式內部指派的屬性與最初觸發 setter 的屬性不同。getter 也是如此。

js
class Person {
  constructor() {}
  set name(name) {
    this._name = name;
  }
  get name() {
    return this._name;
  }
}
const tony = new Person();
tony.name = "Tonisha";
console.log(tony);

參見