◆ 末尾再帰の最適化が最新 Chromium で使えます
  ◆ harmony オプションが必要です
◆ 末尾再帰の関数ならスタックサイズオーバーに悩まされることがなくなります
◆ 便利な反面 無限ループという別の問題も

ES6 の機能でちょっと特殊な末尾再帰の最適化 Chromium で使えるようになってました
最新の

Chrome 50.0.2649.0
V8 4.10.200

--js-flags=--harmony-tailcalls

で確認してます

function recur(x, acc){
if(x <= 0) return acc
return recur(x - 1, acc + x)
}
recur(100, 0) // 5050
recur(1000000, 0) // 500000500000

ところで今の Chrome だと

Uncaught RangeError: Maximum call stack size exceeded


これまでの JavaScript だとよくこの スタックサイズオーバーなエラーになっていましたがこれからは再帰をどんどん書いていけるということですね
でもその分 再帰の条件ミスがあってもスタックサイズオーバーで止まってくれないので 無限ループになると考えるとやっかいなものともいえます


今のところは コンソールで入力したコードを開発者ツールが有効な状態で実行しても最適化されずエラーになります
ソースに書かれているコードは開発者ツールが開かれていても最適化が効いています

動作確認のためにコンソールで実行してもエラーになるのはちょっと不便です
コンソールを閉じているときに関数が実行されると大丈夫なので ボタンのイベントに設定したり setTimeout を設定して実行が始まるときは開発者ツールを閉じておくという手もあります
不便なのは変わらないですけどね

そもそも末尾再帰って

今回の機能はあくまで 末尾再帰 の最適化
だそうです

なので全部の再帰関数が無限に再帰ができるってことではありません

末尾再帰というのは名前通り 末尾 つまり関数の最後で再帰関数を呼び出しているものです
// OK
function recur(r){
if(r < 0) return "complete"
return recur(r - 1)
}
// NOT OK
function recur(r){
if(r < 0) return "complete"
var x = recur(r - 1)
return x + "!"
}

上の OK な方は return のあとは直接文字列か再帰関数呼び出しです
下の OK じゃない方は 再帰関数の実行したあとに何か処理が入って return しています

普通の再帰関数の呼び出しはこんなのですよね
一番深い recur まで言ったら順に戻ってきて最初の呼び出し元の main まで戻ります
main
↓  ↑
recur
↓  ↑
recur
↓  ↑
recur

関数の呼び出し元に戻るための情報はこんなです
main
↓  ↑
recur (main の 10 行目に戻る)
↓  ↑
recur (recur の 2 行目に戻る)
↓  ↑
recur (recur の 2 行目に戻る)

このときに再帰呼び出し後の上矢印のところで 何もすることなくそのまま上に戻るだけを繰り返すのだったら 再帰中のそれぞれの関数の内部の情報(ローカル変数とか)を保存しておく必要はなくなります

それなら 関数を呼び出すときに元々の呼び出し元の情報を渡しておけば
main

recur (main の 10 行目に戻る)

recur (main の 10 行目に戻る)

recur (main の 10 行目に戻る)
main
↓  ↑
recur  |
↓  |
recur  |
↓  |
recur (main の 10 行目に戻る)

途中を通さず一気に戻れるというわけです

関数の戻り先の情報なんて普段 JavaScript 使ってて気にするようなところじゃないので分かりづらいですが 機械語 アセンブリ言語とかの低レイヤーな部分で関数呼び出しがどうなってるのかイメージすればわかるのかも

この図だと recur が呼び出した数だけ残っていますが いま実行中の recur 以外は消してしまっても良いので 実質無限に呼び出せるということです

もし 関数呼び出したあとで足し算したり何かの処理がある場合は全部の recur を保存しておかないとだめなので 最適化ができずに呼び出せる限界があります


作るものによっては 末尾再帰にできないときもあると思いますが できるなら末尾再帰にしてみると良いと思います
何重にも呼び出した関数を 1 つ 1 つ戻る必要がないので速度もちょっとは早くなってそうです

ただ 最初の方にも書いた条件ミスで無限ループっていうのが怖いのでそこは気をつけないとです