首頁  >  文章  >  web前端  >  javascript函數表達式的特徵以及遞歸的理解(附範例)

javascript函數表達式的特徵以及遞歸的理解(附範例)

不言
不言轉載
2018-10-25 15:40:361847瀏覽

這篇文章帶給大家的內容是關於javascript函數表達式的特徵以及遞歸的理解(附範例),有一定的參考價值,有需要的朋友可以參考一下,希望對你有幫助。

函數表達式是JavaScript中的一個既強大又容易令人困惑的特性。

定義函數的方式有兩種: 一種是函數聲明,另外一種就是函數表達式。

函數宣告的語法是這樣的。

function functionName(arg0, arg1, arg2) {
    //函数体
}

語法:首先是function關鍵字,然後是函數的名字,這就是指定函數名稱的方式。

Firefox、Safari、Chrome和Opera都為函數定義了一個非標準的name屬性,透過這個屬性可以存取到給函數指定的名字。
這個屬性的值永遠等於跟在function關鍵字後面的識別符。

//只在Firefox、Safari、Chrome和Opera有效
function functionName(arg0, arg1, arg2) {
}
console.log(functionName.name); // "functionName"

關於函數聲明,它的一個重要特徵是函數聲明提升(function declaration hoisting),意思是在執行程式碼之前會先讀取函數宣告。這就意味著可以把函數宣告放在呼叫它的語句後面。

sayName(); // "Shaw"
function sayName(){
    console.log("Shaw");
}

這個範例不會拋出錯誤,因為在程式碼執行之前會先讀取函數宣告

第二個建立函數的方式是使用函數表達式。

函數表達式有幾種不同的語法形式。
下面是最常見的一種形式。

var functionName =  function(arg0, arg1, arg2) {
    //functionBody
};

這種形式看起來好像是常規的變數賦值語句,即建立一個函數並將它賦值給變數functionName。

這種情況下建立的函數叫做 匿名函數(anonymous function), 因為function關鍵字後面沒有識別符。
匿名函數也叫拉姆達函數。匿名函數的name屬性是空字元竄。

函數表達式與其他表達式一樣,在使用前必須先賦值。

sayHi(); // error : sayHi is not a function
var sayHi = function(){
    console.log("Hi");
}
// var sayHi //此时sayHi是undefined
// sayHi() // error : sayHi is not a function
// sayHi = function() { console.log("Hi");}

理解函數提升的關鍵,就是理解函數宣告與函數表達式之間的差異。

能夠建立函數再賦值給變量,也就能夠把函數當作其他函數的值回傳。

function createComparisonFunction(propertyName) {
    return function(object1, object2) {
        var value1 = object1[propertyName];
        var value2 = object2[propertyName];
        if(value1 < value2) {
            return -1
        }else if(value1 > value2) {
            return 1;
        } else {
            return 0;
        }
    }
}

createComparisonFunction()傳回了一個匿名函數。
傳回的函數可能會被賦值給一個變量, 或以其他方式被呼叫。
不過,在createComparisonFunction()函式內部,它是匿名的。
在把函數當成值的情況下,都可以使用匿名函數。
不過,這並不是匿名函數唯一的用途。

遞迴

遞迴函數就是一個函數透過函數名稱呼叫自身的情況下構成的。

function factorial(num) {
    if(num <= 1) {
        return 1;
    } else {
        return num * factorial(num-1);
    }
}
factorial(4); // 4*3*2*1 = 24

//4* factorial(3) => 4*3*factorial(2) => 4*3*2*factorial(1) => 4*3*2*1 => 24

這是一個經典的遞歸階乘函數。雖然這個函數表面看來沒什麼問題,但下面的程式碼可能導致它出錯。

function factorial(num) {
    if(num <= 1) {
        return 1;
    } else {
        return num * factorial(num-1);
    }
}
var anotherFactorial = factorial;
factorial = null;
//注意这里,其实函数factorial指向一个空对象。
console.log(anotherFactorial(4));  //Error: anotherFactorial is not a function

以上程式碼先把factorial()函數保存在變數anotherFactorial中,然後將factorial變數設為null,結果指向原始函數的參考只剩下一個。
但接下來呼叫anotherFactorial()時,必須執行factorial(),而factorial已經不是函數, 所以就會導致錯誤。

Google Chrome測試了上述程式碼,是不行的, 建議不用深入了解這部分的內容。
在這種情況下,使用arguments.callee可以解決這個問題。

arguments.callee是一個指向正在執行的函數的指針,因此可以用它來實現對函數的遞歸呼叫。

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

“return num * arguments.callee(num-1);”  透過使用arguments.callee代替函數名,可以確保無論怎麼呼叫函數都不出問題。
因此,在編寫遞歸函數時,使用arguments.callee總比使用函數名稱更保險。

但在嚴格模式下,不能透過腳本存取arguments.callee, 存取這個屬性會導致錯誤。

不過我們可以使用命名函數表達式來達成相同的成果。

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

//factorial 指向了函数f

var anotherFactorial = factorial;
//anotherFactorial 指向了函数f

factorial = null;
//factorial 指向了一个空对象。

anotherFactorial(4); //24
////anotherFactorial 指向了函数f, 所以还可以正常调用。

以上程式碼創建了創建了一個名為f()的命名函數表達式,然後將它賦值給變數factorial。
即便把函數賦值給另一個變量,函數的名字f仍然有效,所以遞歸呼叫照樣能正確完成。
這種方式在嚴格模式和非嚴格模式下都行得通。

#

以上是javascript函數表達式的特徵以及遞歸的理解(附範例)的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:segmentfault.com。如有侵權,請聯絡admin@php.cn刪除