JavaScript尾调用函数是否已优化?

Adi*_*ngh 51 javascript recursion tail-recursion

我一直试图Tail call optimization在JavaScript的上下文中理解并编写了下面的递归和尾递归方法factorial().

递归:

function factorial (n) {
  if (n < 2) {
    return 1;
  } else {
    return n * factorial(n-1);
  }
}
Run Code Online (Sandbox Code Playgroud)

尾递归:

function factorial (n) {
  function fact(n, acc) {
    if (n < 2) {
      return acc;
    } else {
      return fact(n-1, n * acc);
    }
  }

  return fact(n, 1)
}
Run Code Online (Sandbox Code Playgroud)

但我不确定tail-recursive该函数的版本是否会被JavaScript编译器优化,因为它是在Scala等其他语言中完成的.有人可以帮我解决这个问题吗?

she*_*tme 36

更新:截至2018年3月13日,Safari是唯一支持尾部呼叫优化的浏览器.

铬团队明确指出Tail Call Optimization未进行积极开发,可在此处进行跟踪.

可以在此处跟踪Firefox的实现

原帖

是的,ES2015在严格模式下提供尾调用优化.Axel Rauschmayer博士在下面的链接中精美地展示了它,所以我不会在这里重复他的话.

注意:ES 5不优化尾调用.

http://www.2ality.com/2015/06/tail-call-optimization.html

  • 在ES2015中进行尾调用优化与所有或甚至大多数实现明显不同,否则这些实现声称符合ES2015标准,进行适当的尾调用优化."JavaScript函数尾调优化?"问题的正确答案.是"如果引擎发生了,它们就是它们,否则就不是." 请参阅https://www.chromestatus.com/feature/5516876633341952. (13认同)
  • 我很确定浏览器在为ES6实现ES5代码时也会尾随调用优化ES5代码.只是ES6规范现在明确要求它(并且仍然没有引擎实现它). (5认同)

AK_*_*AK_ 12

理论上是的.正如其他答案所述.

但在实践中,截至2017年7月,只有Safari支持它.

Javascript ES6(ES2015)兼容性:https://kangax.github.io/compat-table/es6/

  • 正确 - 由于Chrome根本没有使用它,因此尾部调用在生产代码中永远不可用的可能性非常大.https://www.chromestatus.com/feature/5516876633341952 (2认同)

tjj*_*fvi 10

正如其他答案所说,实际上并非如此。但是,您可以定义一个实用程序来提供帮助。

class Tco {
  constructor(func) {
    this.func = func;
  }
  execute() {
    let value = this;
    while (value instanceof Tco)
      value = value.func();
    return value;
  }
}

const tco = (f) => new Tco(f);
Run Code Online (Sandbox Code Playgroud)
function factorial (n) {
  const fact = (n, acc) => tco(() => {
    if (n < 2) {
      return acc;
    } else {
      return fact(n-1, n * acc);
    }
  });

  return fact(n, 1).execute();
}

console.log(factorial(2000000)); // Infinity
Run Code Online (Sandbox Code Playgroud)

如您所见,这允许您编写仅在语法上有很小差异的尾递归函数,而不会遇到最大调用堆栈错误。

  • 我不知道这是否是一个好主意。但这绝对是 JavaScript 中的 TCO。干得好! (5认同)