InternalError: 递归过多

当函数调用过多或函数缺少基本情况时,会发生 JavaScript 异常“递归过多”或“超出最大调用栈大小”。

消息

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" is the exit condition
    return;
  // do stuff
  loop(x + 1); // the recursive call
}
loop(0);

将此条件设置为极高的值将不起作用。

js
function loop(x) {
  if (x >= 1000000000000) return;
  // do stuff
  loop(x + 1);
}
loop(0);

// InternalError: too much recursion

此递归函数缺少基本情况。由于没有退出条件,因此函数将无限地调用自身。

js
function loop(x) {
  // The base case is missing
  loop(x + 1); // Recursive call
}

loop(0);

// InternalError: too much recursion

类错误:递归过多。

js
class Person {
  constructor() {}
  set name(name) {
    this.name = name; // Recursive call
  }
}

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

将值分配给属性名称 (this.name = name;) 时,JavaScript 需要设置该属性。发生这种情况时,会触发 setter 函数。

在此示例中,当触发 setter 时,它被告知要再次执行相同的操作:设置它旨在处理的相同属性。这会导致函数一次又一次地调用自身,使其无限递归。

如果在 getter 中使用相同的变量,也会出现此问题。

js
class Person {
  get name() {
    return this.name; // Recursive call
  }
}

为避免此问题,请确保在 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);

另请参阅