圖片來源:Udemy
在這堂課中,我們會說明 function statements 和 function expressions 這兩種不同建立函式的方式,這是兩個許多新手在學 JavaScript 會有些搞不懂的地方。
在進入到 function 的部分前,先來看看表達式(expressions)和陳述句(statements)有什麼不同吧。
表達式(Expressions)和陳述句(Statements)的差異
表達式(Expressions)
Expressions 指的是輸入後能夠直接回傳值的一串程式(a unit of code that results in a value),我們一般可能會把它存成一個變數,但是它不一定要被存成一個變數。
簡單來說,只要你輸入的那串程式執行後能直接回傳一個值,那麼它就是個 expression。
舉例來說,在瀏覽器的 console 中輸入 a = 3 時,它會直接回傳 3 這個值;輸入 2 + 3 的時候,它會直接回傳 5;輸入 a = { } 的時候,它會回傳一個為物件的值。這種輸入一段程式後,會直接取得回傳一個值的程式內容,我們就稱為 Expressions。
陳述句(Statements)
那麼陳述句(Statements)是什麼呢?
我們來看看這段程式:
if (a === 3) { console.log('Hello'); }
在這段程式中 a === 3 是一個表達式(expression),因為它可以直接回傳值(即,true 或 false);而 if 這個指令,則是一個 statement,因為它不會直接回傳一個值,我們也不能將它指定為一個變數:
// ❌ 錯誤寫法 const b = if (a === 3) { console.log('Hello'); }
Function Expressions 和 Function Statements
在 JavaScript 中,Function 就是物件的一種,它可以透過 Expression 或 Statements 的方式加以操作。
Function Statements
首先,我們來看一下Function Statements:
// Function Statements function greet() { console.log('Hi'); }
這是一串 Function Statements,它不會直接回傳任何的值。Function Statements 的特色在於,它在程式執行的最開始,該函式就會透過 hoisting 先被儲存在記憶體中(如果不清楚 Hoisting 的概念,可以參考:筆記 - 談談Javascript 中的 Hoisting),也就是說我們可以在執行這段程式前,就去呼叫這個函式來使用而不會出現任何的錯誤,像是這樣:
/* Function Statements will be hoisted */ greet(); // 'Hi' function greet() { console.log('Hi'); }
由於在 JavaScript 中 function 就像物件一樣,用物件的概念來理解函式的話,這個函式屬性 name 的值是 greeting,這個函式 code 屬性的值為 console.log('hi')。
當我們要執行這個函式的時候,只要輸入 greet() 就可以了。
如果我們用 console.log(greet),會得到這個函式的程式內容(code),所以如果我們想要執行這個函式,就在最後面再加上 ( ) 就可以了:
Function Expressions
再來看一下 Function Expressions,我們先前有提過在 JavaScript 中 Function 就是物件的一種,所以我們可以把它存在一個變數中。以下面的程式碼為例:
// Function Statements const sayHello = function() { console.log('Hello'); };
這裡的 function(){ ... } 這段就是 Function Expression,現在我們則把這個函式表達式的值存在 sayHello 這個變數內。
從物件的角度來看函式的話,在這個例子中,就是先建立了一個函式,但在這個函式的 name 屬性並沒有給它值(因為我們在括號前面並沒有給任何名稱),之所以可以這麼做是因為,我們在 function expression 前面已經把它指定到一個變數(sayHello)了,所以可以直接用這個變數名稱來指稱這個函式。對於這種 name 屬性沒有值的函式,我們可以稱作匿名函式(anonymous function 或 function literal)
圖片來源:Udemy - JavaScript: Understanding the Weird Parts,此圖的函式名稱為anonymousGreet。
同樣的,如果我們想要執行這個函式,一樣輸入 sayHello() 就可以了。
當我們在瀏覽器的開發者工具中輸入 console.log(sayHello) 時,我們一樣會得到這個函式的程式內容,因此,若我們想要執行這段程式內容,同樣只要在最後面加上( ) 就可以了:
然而,和 Function Statements 不同的地方是,因為在一開始執行程式初期,只會先建立並儲存變數名稱到記憶體中,也就是只會儲存 sayHello 到記憶體中,但程式內容不會一併儲存進去(這時候 sayHello 的值會是 undefined),所以如果我在 function 定義前面就想要執行它的話,即會出現錯誤訊息:
// ❌ 錯誤寫法 // ReferenceError: Cannot access 'sayHello' before initialization sayHello(); // 使用 Function Statements 的話不能在定義前呼叫它 // Function Statements const sayHello = function() { console.log('Hello'); };
再來看一點特別的吧 - 函式中的函式
看一下這段程式。先建立一個函式,name 屬性的值是 log,code 的內容是 console.log(a),其中 a 是這個函式的參數。
接著,我分別去執行 log(...),會分別得到如下註解的結果:
function log(a) { console.log(a); } log(3); // 3 log('Hello'); // 'Hello' log({ Country: 'Taiwan' }); // { Country: 'Taiwan' }
同樣地,我們也可以先把這些值指定成一個變數,然後再丟到函式當中,會得到一樣的結果:
function log(a) { console.log(a); } const number = 3; log(number); // 3 const hello = 'Hello'; log(hello); // 'Hello' const country = { Country: 'Taiwan' }; log(country); // { Country: 'Taiwan' }
如果這時候我的值是函式的話呢
假設我們在 log() 裡面,放入一個 function expressions,而且是一個匿名函式(anonymous function),這時候我們等於是直接創造了一個函式來使用(create the function on the fly):
log(function() { console.log('Hello'); });
如果看不太懂的話,可以想成這樣會比較容易理解:
const anonymousFunction = function() { console.log('Hello'); }; log(anonymousFunction);
這時候會回傳這樣的結果:
那麼,如果我們希望能夠直接執行該函式,我們可以將 log( ) 做一下簡單的修改就可以了:
function log(a) { a(); } const anonymousFunction = function() { console.log('Hello'); }; log(anonymousFunction); // Hello
這樣,就可以在不用建立函式的情況下,直接去執行一個匿名函式。
由於 JavaScript 非同步的特性,若想要確保程式執行的順序,常常會使用到回呼函式(callback function)這種方式,它內部的做法其實也就是把函式傳入另一個函式中去呼叫。
這種傳入一個函式到另一個函式中去呼叫的方式在 JavaScript 中經常會使用到,特別是回呼函式(callback function)。
程式範例
// Function Statements function greet() { console.log('Hi'); } // Function Expressions var sayHello = function() { console.log('Hello'); }; function log(a) { a(); } log(function() { console.log('Hello'); });
資料來源
Udemy - JavaScript: Understanding the Weird Parts- Function Statements and Function Expressions
0 意見:
張貼留言