[JavaScriptWeird]No.26 函式表示式與函式陳述句

前言

現在我們知道,在 JavaScript 內函數就是物件,接著要運用這個觀念,來做一些實際運用!但在開始之前,需要先了解函式陳述句 (Function Statements)、什麼是函式表示式 (Function Expressions) 之間的用法差異。

表示式 (Expressions)

表示式為程式碼的單位,會回傳一個值 (A unit of code that results in a value)

白話來說,函式表示式或者任何形式的表示式最終會創造一個值,然而這個值不一定要儲存在某個變數,且這個值可以是任何東西。

舉個例子,我們在 .js 檔內宣告變數 a 並且打開開發者工具,輸入以下:

這是一個簡單的表示式,我們把 3 透過等號運算子賦予給變數 a ,並且執行它,得到回傳的結果。

我們說過,值不一定要儲存在某個變數,因此這樣也是表示式:

這個表示式回傳了 3 ,我們並沒有使用等號運算子將這個值放入變數。

然而,表示式也可以是這個形式

陳述句 (Statements)

當我們提到陳述句,陳述句代表「會做某件事」。

1
2
3
4
var a;  
if (a === 3){

}

if (a === 3) 就做某件事情。

在 if 陳述句的括號內,必須放進表示式產生一個值,這樣這個陳述句才能運作。

另外陳述句本身不會回傳任何值。

像是我不能這麼做,這是無效的,因為沒有任何值會被回傳給變數 b 。

1
2
3
4
// 錯誤示範  
var b = if (a === 3){

}

簡單來說,陳述句會做其他事;表示式則回傳值

函式表示式與函式陳述句的差異

接著我們來看看這兩者之間的差異,直接看例子。

函式陳述句

1
2
3
function greet() {  
console.log('Hi');
}

這樣就是一個簡單的函式陳述句,當創造執行環境時, greet 函式被放進記憶體中,但因為 greet 是函式陳述句,所以不會回傳任何值,直到函式被呼叫執行。

雖然函式陳述句不會回傳任何值,但它會有提升 (hoisting) 現象,所以可以在任何地方取用它,像這樣:

1
2
3
4
greet();  
function greet() {
console.log('Hi');
}

函式表示式

1
2
3
var anonymousGreet = function greet() {  
console.log('Hi');
}

宣告一個 anonymousGreet 變數並且使用等號運算子,然後在右側使用函式陳述句。

記得我們說的「函式就是物件」,所以可以當作「我們建立了一個物件,並設定它等於這個變數」,也就是這個變數在記憶體中指向的位址。

另外,我們已經有一個已經知道函式物件位址的變數 anonymousGreet ,所以等號右邊的陳述句可以改寫成這樣,稱為匿名函式。

1
2
3
var anonymousGreet = function() {  
console.log('Hi');
}

如何觸發函式表示式呢

我們需要指向該物件,並且告訴它執行程式,像是這樣

1
anonymousGreet();

因為變數已經知道了函式物件的記憶體位址,只需要加上()來呼叫函式就可以執行了。

另外還有一個值得注意的問題,函式表示式的提升 (hoisting) 現象,如果我們將程式改成這個樣子:

1
2
3
4
anonymousGreets();  
var anonymousGreets = function() {
console.log('Hi');
}

結果會變成這樣,為什麼呢?

還記得當執行環境被創造,創造執行階段會把函式陳述句以及變數都放入記憶體,變數被賦予初始值 undefined ,然後逐行執行程式碼。

於是程式的第一行是「anonymousGreets();」,但此時仍未賦予變數值,變數的值仍然是 undefined 。自然的,錯誤便會告訴我們 undefined 不是函式,它沒辦法被使用 () 呼叫執行。

1
2
3
var anonymousGreets = function() {  
console.log('Hi');
}

直到上述這行程式碼,anonymousGreets 變數的值才被賦予函式物件。

代表函式表示式不受到提升 (hoisting) 影響。

傳入函式表示式做為參數

記得我們說的函式是物件,函式表示式可以馬上創造函式物件,因此我們可延伸出以下寫法:

1
2
3
4
5
6
7
function log(a){  
console.log(a);
}

log(function(){
console.log('hi');
});

我們立即創造了一個函式物件,在裡面寫了一些程式碼。然後把這個函式物件當成參數傳入 log 函式內。

不過這樣只是印出函式物件的內容而已,但透過這樣的觀察得知「一級函式可以很快地被創造、使用,且變數也可以設值成為一級函式

我們結合上述這些並做些修改:

1
2
3
4
5
6
7
8
function log(a){  
a();
}

var anonymousGreets = function(){
console.log('hi');
}
log(anonymousGreets); // hi

因為我們傳入 log 函式的參數為函式物件,所以變數 a 參照到了這個函式物件。同樣地,要呼叫執行函式僅需要加上 () 即可。

本例來看,我使用函式表示式,接著把這個函式傳入當作另一個函式的參數,這樣另一個函式就可以使用這個函式表示式,這就是我們提到的一級函式的觀念「可以將函式傳入別處」。

可以把函式給另一個函式,就像使用變數一樣,這樣的做法也稱為函式程式語言 ( functional programming )

0%