這裏有兩個相當,不同的問題:
- 或不Node.js的做TCO?
- 在Node.js中,這個神奇的產量是如何工作的?
或不Node.js的做TCO?
TL; DR:不一樣了,因爲節點8.x的的。它已經有一段時間,落後於一面旗幟或另一面旗幟,但截至撰寫本文時(2017年11月),它已經不復存在了,因爲它使用的底層V8 JavaScript引擎不再支持TCO。有關更多信息,請參閱this answer。
詳情:
尾部調用優化(TCO)是必需的part of the ES2015 ("ES6") specification。因此,直接支持它並不是NodeJS,它是NodeJS使用的V8 JavaScript引擎需要支持的東西。
從節點8.x開始,V8不支持TCO,甚至不支持標誌。它可能會在未來的某個時候再次發生;有關更多信息,請參閱this answer。
節點7.10下降到6.5.0至少(我的筆記說6.2,但不同意node.green)只在嚴格的模式下支持TCO標誌後面(--harmony
在6.6.0及以上,--harmony_tailcalls
更早)。
如果要檢查您的安裝,下面是測試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());
$ # Only certain versions of Node, notably not 8.x or (currently) 9.x; see above
$ node --harmony tco.js
true
true
如何這個神奇的yield
事情在Node.js中工作?
這是另一個ES2015的東西(「發電機功能」),所以它也是V8必須實現的東西。它完全在Node 6.6.0的V8版本中實現(並且已經有多個版本),並且沒有落後於任何標誌。
生成器函數(用function*
和yield
編寫的函數)能夠停止並返回捕獲其狀態並可用於在後續場合繼續其狀態的迭代器。亞歷克斯Rauschmeyer有他們的深入文章here。
下面是使用由生成器函數明確返回的迭代器的例子,但你通常不會做到這一點,我們會看到,爲什麼在一個時刻:
"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);
}
具有此輸出:
0
1
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
和value
性能是所有樣板是(通常)獲取的是我們正在努力做的方式,因此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
檢查我們。
用'--harmony'標誌運行節點來查看你的第二個版本是如何工作的。例如'node --harmony mytest.js'。但首先重新審視你引用的例子,你只是將它的一部分適用於你的案例。關於TCO,真正的問題是V8是否已經實現了它 - 並且在[v8更新日誌]中沒有提及已經完成的工作(https://code.google.com/p/v8/source/browse/trunk/ChangeLog ),我可以看到。 –
@ barry-johnson:我嘗試在第二個鏈接中使用「yield」複製樣例函數,而Node.js對「function *」異常。這是我困惑的原因之一。 –
這就是爲什麼我說你需要使用--harmony選項運行節點。生成器是ES6/Harmony的一部分,它不是節點的默認設置。 –