PJCHENder 未整理筆記

[JS] 進一步談 JavaScript 中函式的建立 - function statements and function expressions

2019-09-24

[筆記] 進一步談 JavaScript 中函式的建立 - function statements and function expressions

@([Udemy] JavaScript: Understanding the Weird Parts)[javaScript, udemy]

img

圖片來源: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。

Imgur

陳述句(Statements)

那麼陳述句(Statements)是什麼呢?

我們來看看這段程式:

1
2
3
if (a === 3) {
console.log('Hello');
}

在這段程式中 a === 3 是一個表達式(expression),因為它可以直接回傳值(即,truefalse);而 if 這個指令,則是一個 statement,因為它不會直接回傳一個值,我們也不能將它指定為一個變數:

1
2
3
4
// ❌ 錯誤寫法
const b = if (a === 3) {
console.log('Hello');
}

Function Expressions 和 Function Statements

在 JavaScript 中,Function 就是物件的一種,它可以透過 Expression 或 Statements 的方式加以操作。

Function Statements

首先,我們來看一下Function Statements:

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

這是一串 Function Statements,它不會直接回傳任何的值。Function Statements 的特色在於,它在程式執行的最開始,該函式就會透過 hoisting 先被儲存在記憶體中(如果不清楚 Hoisting 的概念,可以參考:筆記 - 談談Javascript 中的 Hoisting),也就是說我們可以在執行這段程式前,就去呼叫這個函式來使用而不會出現任何的錯誤,像是這樣:

1
2
3
4
5
6
/* Function Statements will be hoisted */
greet(); // 'Hi'

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

由於在 JavaScript 中 function 就像物件一樣,用物件的概念來理解函式的話,這個函式屬性 name 的值是 greeting,這個函式 code 屬性的值為 console.log('hi')

imgur

圖片來源:Udemy - JavaScript: Understanding the Weird Parts

當我們要執行這個函式的時候,只要輸入 greet() 就可以了。

如果我們用 console.log(greet),會得到這個函式的程式內容(code),所以如果我們想要執行這個函式,就在最後面再加上 ( ) 就可以了:

Imgur

Function Expressions

再來看一下 Function Expressions,我們先前有提過在 JavaScript 中 Function 就是物件的一種,所以我們可以把它存在一個變數中。以下面的程式碼為例:

1
2
3
4
// Function Statements
const sayHello = function() {
console.log('Hello');
};

這裡的 function(){ ... } 這段就是 Function Expression,現在我們則把這個函式表達式的值存在 sayHello 這個變數內。

從物件的角度來看函式的話,在這個例子中,就是先建立了一個函式,但在這個函式的 name 屬性並沒有給它值(因為我們在括號前面並沒有給任何名稱),之所以可以這麼做是因為,我們在 function expression 前面已經把它指定到一個變數(sayHello)了,所以可以直接用這個變數名稱來指稱這個函式。對於這種 name 屬性沒有值的函式,我們可以稱作匿名函式(anonymous function 或 function literal)

imgur

圖片來源:Udemy - JavaScript: Understanding the Weird Parts,此圖的函式名稱為anonymousGreet

同樣的,如果我們想要執行這個函式,一樣輸入 sayHello() 就可以了。

當我們在瀏覽器的開發者工具中輸入 console.log(sayHello) 時,我們一樣會得到這個函式的程式內容,因此,若我們想要執行這段程式內容,同樣只要在最後面加上( ) 就可以了:

Imgur

然而,和 Function Statements 不同的地方是,因為在一開始執行程式初期,只會先建立並儲存變數名稱到記憶體中,也就是只會儲存 sayHello 到記憶體中,但程式內容不會一併儲存進去(這時候 sayHello 的值會是 undefined),所以如果我在 function 定義前面就想要執行它的話,即會出現錯誤訊息:

1
2
3
4
5
6
7
8
9
// ❌ 錯誤寫法
// ReferenceError: Cannot access 'sayHello' before initialization

sayHello(); // 使用 Function Statements 的話不能在定義前呼叫它

// Function Statements
const sayHello = function() {
console.log('Hello');
};

imgur

再來看一點特別的吧 - 函式中的函式

看一下這段程式。先建立一個函式,name 屬性的值是 logcode 的內容是 console.log(a),其中 a 是這個函式的參數。

接著,我分別去執行 log(...),會分別得到如下註解的結果:

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

log(3); // 3
log('Hello'); // 'Hello'
log({ Country: 'Taiwan' }); // { Country: 'Taiwan' }

同樣地,我們也可以先把這些值指定成一個變數,然後再丟到函式當中,會得到一樣的結果:

1
2
3
4
5
6
7
8
9
10
11
12
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):

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

如果看不太懂的話,可以想成這樣會比較容易理解:

1
2
3
4
5
const anonymousFunction = function() {
console.log('Hello');
};

log(anonymousFunction);

這時候會回傳這樣的結果:

Imgur

那麼,如果我們希望能夠直接執行該函式,我們可以將 log( ) 做一下簡單的修改就可以了:

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

const anonymousFunction = function() {
console.log('Hello');
};

log(anonymousFunction); // Hello

這樣,就可以在不用建立函式的情況下,直接去執行一個匿名函式。

由於 JavaScript 非同步的特性,若想要確保程式執行的順序,常常會使用到回呼函式(callback function)這種方式,它內部的做法其實也就是把函式傳入另一個函式中去呼叫。

這種傳入一個函式到另一個函式中去呼叫的方式在 JavaScript 中經常會使用到,特別是回呼函式(callback function)。

程式範例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 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

掃描二維條碼,分享此文章