Node.js尾部调用优化:可能吗?
到目前为止,我一直喜欢JavaScript,并决定使用Node.js作为我的引擎,它声称Node.js提供了TCO。但是,当我尝试使用Node.js运行此(显然是尾部调用)代码时,会导致堆栈溢出:
function foo(x) { if (x == 1) {
return 1;
}
else {
return foo(x-1);
}
}
foo(100000);
现在,我做了一些挖掘,发现了这一点。在这里,看来我应该这样写:
function* foo(x) { if (x == 1) {
return 1;
}
else {
yield foo(x-1);
}
}
foo(100000);
但是,这给了我语法错误。我试过它的各种排列,但在所有的情况下,Node.js的似乎不满 的东西 。
本质上,我想了解以下内容:
- Node.js是否执行TCO?
- 这个神奇的
yield
东西如何在Node.js中工作?
回答:
这里有两个相当不同的问题:
- Node.js是否执行TCO?
- 这个神奇的yield事物如何在Node.js中工作?
: 。它做了一段时间了,一个或另一个,但是在撰写本文时(2017年11月),它不再存在,因为它使用的底层V8JavaScript引擎不再支持TCO。。
细节:
尾呼叫优化(TCO)是ES2015(“ES6”)规范的必需部分。因此,直接支持它不是NodeJS,而是NodeJS使用的V8 JavaScript引擎需要支持的东西。
从Node8.x开始,V8不支持TCO,甚至不支持标记。它可能会在将来的某个时候再次出现。。
节点7.10至少降至6.5.0(我的注释说为6.2,但node.green表示不同意)仅在严格模式下(--harmony
在6.6.0及更高--harmony_tailcalls
版本中)标记后面支持TCO
。
如果要检查安装,请使用node.green使用的测试(如果使用的是相关版本,请确保使用该标志):
function direct() { "use strict";
return (function f(n){
if (n <= 0) {
return "foo";
}
return f(n - 1);
}(1e6)) === "foo";
}
function mutual() {
"use strict";
function f(n){
if (n <= 0) {
return "foo";
}
return g(n - 1);
}
function g(n){
if (n <= 0) {
return "bar";
}
return f(n - 1);
}
return f(1e6) === "foo" && f(1e6+1) === "bar";
}
console.log(direct());
console.log(mutual());
$#仅适用于某些版本的Node,尤其不是8.x或(当前)9.x;往上看
$ node --harmony tco.js
真正
真正
这是ES2015的另一件事(“生成器功能”),因此也是V8必须实现的东西。它已在Node
6.6.0中的V8版本中完全实现(并且已经有多个版本),并且没有任何标志。
生成器函数(使用编写function*
和使用的函数yield
)能够停止并返回捕获其状态的迭代器,并可以在以后的情况下继续其状态,从而发挥作用。AlexRauschmeyer在这里对它们进行了深入的介绍。
这是一个使用显式生成器函数返回的迭代器的示例,但是您通常不会这样做,稍后我们将看到原因:
"use strict";function* counter(from, to) {
let n = from;
do {
yield n;
}
while (++n < to);
}
let it = counter(0, 5);
for (let state = it.next(); !state.done; state = it.next()) {
console.log(state.value);
}
具有以下输出:
01个
2
3
4
运作方式如下:
- 当我们调用
counter
(let it = counter(0, 5);
)时,对的初始内部状态counter
已初始化,我们立即获得了一个迭代器。counter
尚未运行任何实际代码(尚未)。 - 调用通过第一条语句向上
it.next()
运行代码。此时,暂停并存储其内部状态。返回带有标志和的状态对象。如果标志为,则为语句产生的值。counter``yield``counter``it.next()``done``value``done``false``value``yield
- 每次调用都会
it.next()
将状态转发counter
到下一个状态yield
。 - 当一个呼叫
it.next()
品牌counter
完成并返回时,状态对象,我们取回已done
设置true
和value
设置的返回值counter
。
拥有迭代器和状态对象的变量以及调用it.next()
和访问done
andvalue
属性都是(通常)阻碍我们通常试图做的事情的样板,因此ES2015提供了一条新for-of
语句来将其全部删除我们,并给我们每个价值。这是上面用编写的相同代码for-of
:
"use strict";function* counter(from, to) {
let n = from;
do {
yield n;
}
while (++n < to);
}
for (let v of counter(0, 5)) {
console.log(v);
}
v
对应state.value
于我们前面的示例,它完成了for-of
所有的it.next()
调用并done
为我们进行检查。
以上是 Node.js尾部调用优化:可能吗? 的全部内容, 来源链接: utcz.com/qa/401537.html