尾調用優化(Tail Call Optimization)

原文地址: http://www.cnblogs.com/xfshen/p/6001581.html


尾調用優化(Tail Call Optimization)

尾調用是指函數的最後一條語句是函數調用,比如下面的代碼:

function doSomething() {
    return doSomethingElse();
}

在ES5中,尾調用和其他形式的函數調用一樣:腳本引擎創建一個新的函數棧幀並且壓在當前調用的函數的棧幀上面。也就是說,在整個函數棧上,每一個函數棧幀都會被保存,這有可能造成調用棧佔用內存過大甚至溢出。

尾遞歸在ES6中有何不同(How Tail Call are Different in ECMAScript6)

在ES6中,strict模式下,滿足以下條件,尾調用優化會開啓,此時引擎不會創建一個新的棧幀,而是清除當前棧幀的數據並複用:

  (1、尾調用函數不需要訪問當前棧幀中的變量

  (2、尾調用返回後,函數沒有語句需要繼續執行

  (3、尾調用的結果就是函數的返回值

 

下面例子中的函數就可以使用尾調用優化:

"use strict";

function doSomething() {
    return doSomethingElse();
}

然而下面例子中的函數不能使用尾調用優化,因爲尾調用的結果不是函數的返回值:

"use strict";

function doSomething() {
    doSomethingElse();
}

尾調用返回後,如果函數仍然有語句要執行,也是不能使用尾調用優化的:

"use strict";

function doSomething() {
    return 1 + doSomethingElse();
}

尾調用函數如果是閉包函數,也不能使用尾調用優化:

複製代碼
"use strict";

function doSomething() {
    var num = 1;
    var func = () => num;
    return func();
}
複製代碼

如何利用尾調用優化(How to Harness Tail Call Optimization)

尾調用優化主要用在遞歸函數中,通常能帶來顯著的效果。看一個遞歸計算階乘的函數:

複製代碼
function factorial(n) {
    if (n <= 1) {
      return 1;
    } else {
      return n * factorial(n - 1);
    }
}
複製代碼

很明顯,上面的代碼並不能使用尾調用優化,因爲factorial(n-1)返回後,仍然有語句要繼續執行。但是我們可以改寫這個函數,使其能夠利用尾調用優化特性:

複製代碼
function factorial(n, p = 1) {
    if (n <= 1) {
      return 1 * p;
    } else {
      let result = n * p;
      return factorial(n - 1, result);
    }
}    
複製代碼

如和改寫遞歸函數以便利用尾調用優化是需要技巧的。如果可以,應該充分利用引擎的尾調用優化特性來優化遞歸函數。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章