不積跬步之漫談JavaScript的遞歸函數

最近在看<JavaScript高級程序設計>中看到arguments.callee這個屬性,才知道JavaScript裏面的遞歸有這麼多的坑。之前都不知道,今天就整理一下,咱們先從最初開始吧。

用遞歸實現一個階乘函數

這裏就不用說定義了,我們直接上代碼,在正常模式下。es6

function factorial(num){
    if(num<=1){
        return 1;
    }else{
        return num * factorial(num-1);
    }
}

上面是一個典型的遞歸調用,可可是,它有一個問題。函數

var anotherFactorial = factorial;
factorial = null;
alert(anotherFactorial(4));//報錯

遞歸函數就是在一個函數裏經過名字調用自身的狀況(仍是得說一下)。正常的狀況下咱們這樣作沒有問題了,但是關鍵是JavaScript語言和其餘的語言不太同樣,函數名稱只是一個指針,它並非函數的實體對象。因此若是咱們把這個指針改變了,那麼在函數裏面的指針調用就不是它本身了,上面的例子裏,咱們直接把它幹掉了。讓它指向了空,它直接就奔潰了。優化

這裏就引出了一個重要的屬性:spa

arguments.callee

calleearguments對象的一個屬性,arguments.callee值一個指向正在執行的函數的指針。所以它能夠實現對函數的遞歸調用。設計

例如:代理

function factorial(num){
    if(num<=1){
        return 1;
    }else{
        return num * arguments.callee(num-1);
    }
}

經過使用arguments.callee代理函數名,能夠確保不管怎樣調用函數都不會出問題。所以在正常模式下,使用這種方式會更加保險。指針

可是在嚴格模式下,調用上面的arguments.callee卻會致使錯誤,由於嚴格模式不支持arguments。那怎麼樣辦呢 ?code

嚴格模式下沒法使用arguments.callee怎麼辦 ?

經過使用命名錶達式也但是實現一樣的效果。看代碼:對象

var factorial = (function f(num){
     if(num<=1){
        return 1;
    }else{
        return num * f(num-1);
    }
});

上面的代碼建立了一個名爲f()的命名錶達式,而後將它賦值給變量factorial。這樣的話,即便把變量的變成另外一個變量,也不會影響我內部本身的調用。f函數名字仍然有效。因此遞歸函數調用照樣能正確執行。遞歸

如今咱們把ECMA的版本升級到6.

ES6的狀況下呢?

ES6的狀況下,咱們能夠寫成另外的方式來實現。例如:

const factorial = (function f(num){
     if(num<=1){
        return 1;
    }else{
        return num * f(num-1);
    }
});

咱們把var升級爲const,讓這個變量變成常量,這樣變量也改變不了。咱們用箭頭函數來替代上面的命名函數。

const factorial =num=>{
     if(num<=1){
        return 1;
    }else{
        return num * factorial(num-1);
    }
}

常量沒法更改,因此咱們能夠寫成上面的代碼。可是你覺得代碼就沒有問題了嗎???

咱們這樣調用一下:

factorial(100000);

而後報下面的錯誤,RangeError:超出了最大調用堆棧大小

RangeError: Maximum call stack size exceeded
    at factorial (H:\company_work_space\Demos\Demo1.js:40:18)
    at factorial (H:\company_work_space\Demos\Demo1.js:44:22)
    at factorial (H:\company_work_space\Demos\Demo1.js:44:22)
    at factorial (H:\company_work_space\Demos\Demo1.js:44:22)
    at factorial (H:\company_work_space\Demos\Demo1.js:44:22)
    at factorial (H:\company_work_space\Demos\Demo1.js:44:22)
    at factorial (H:\company_work_space\Demos\Demo1.js:44:22)
    at factorial (H:\company_work_space\Demos\Demo1.js:44:22)
    at factorial (H:\company_work_space\Demos\Demo1.js:44:22)
    at factorial (H:\company_work_space\Demos\Demo1.js:44:22)

下一步就是怎麼解決這個問題。答案是使用尾調用優化。
什麼是尾調用呢?能夠先看這個什麼是尾調用?

相關文章
相關標籤/搜索