|
|
|
|
# JavaScript 入門 - 函式與方法
|
|
|
|
|
|
|
|
|
|
![JavaScript 入門 - 函式](/sketchnotes/webdev101-js-functions.png)
|
|
|
|
|
> 由 [Tomomi Imura](https://twitter.com/girlie_mac) 繪製
|
|
|
|
|
|
|
|
|
|
## 課前測驗
|
|
|
|
|
[課前測驗](https://ashy-river-0debb7803.1.azurestaticapps.net/quiz/9?loc=zh_tw)
|
|
|
|
|
|
|
|
|
|
撰寫程式碼時,我們必須確保程式碼的閱讀性。聽來不太直覺,理解程式碼的時間遠比撰寫時間來的久。裡面最需要被管理的程式項目就是**函式**。
|
|
|
|
|
|
|
|
|
|
[![函式與方法](https://img.youtube.com/vi/XgKsD6Zwvlc/0.jpg)](https://youtube.com/watch?v=XgKsD6Zwvlc "函式與方法")
|
|
|
|
|
|
|
|
|
|
> 點擊上方圖片觀看關於函式的影片。
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## 函式 (Function)
|
|
|
|
|
|
|
|
|
|
函式是程式碼區塊,會在程式執行時被呼叫運行。有些時候我們需要重複性的執行同一項作業,比起複製整個邏輯到其他區塊,函式是較完美的處理方式。不只方便維護,也可以在任何地方、任何時間被其他函式呼叫執行。
|
|
|
|
|
|
|
|
|
|
另一項重點是函式的名稱,聽來不太重要,但它能直接地解釋程式碼的內容。你可以想像它是按鈕上的文字,若按鈕上寫著「停止計時」,你會預期按壓按鈕後會終止計時器的運作。
|
|
|
|
|
|
|
|
|
|
## 建立並呼叫函式
|
|
|
|
|
|
|
|
|
|
函式的語法格式如下:
|
|
|
|
|
|
|
|
|
|
```javascript
|
|
|
|
|
function nameOfFunction() { // 函式的定義
|
|
|
|
|
// 函式的說明與內容
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
如果你想建立一個打招呼的函式,它可能會以下列的格式呈現:
|
|
|
|
|
|
|
|
|
|
```javascript
|
|
|
|
|
function displayGreeting() {
|
|
|
|
|
console.log('Hello, world!');
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
如果你想呼叫這個函式,我們使用函式的名稱加上 `()`。我們不需要考慮函式是在被呼叫地方的前面或後面才被定義出來,JavaScript 的編譯器會幫你尋找它的定義為置。
|
|
|
|
|
|
|
|
|
|
```javascript
|
|
|
|
|
// 呼叫函式
|
|
|
|
|
displayGreeting();
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
> **注意** 另一個你正使用的函式類型稱做 **方法(method)**。事實上,我們能在執行 `console.log` 的 demo 時能找到它。它與函式的差異在於它需要接續在物件後面,在這個例子中就是 `console`,而函式並沒有強制要求的。你會發現許多開發者在兩者之間做切換。
|
|
|
|
|
|
|
|
|
|
### 函式的重點觀念
|
|
|
|
|
|
|
|
|
|
在建立函式時,你需要注意一些重點:
|
|
|
|
|
|
|
|
|
|
- 我們反覆提到的,函式的名字要能了解函式的主要功能。
|
|
|
|
|
- 使用**駝峰式大小寫(camelCasing)**來連接單字。
|
|
|
|
|
- 單一函式只專一在單一功能。
|
|
|
|
|
|
|
|
|
|
## 向含式傳遞資料
|
|
|
|
|
|
|
|
|
|
為了讓函式能被重複利用,你會需要餵給函式不同的資料。以上述 `displayGreeting` 的例子中,它只能輸出文字 **Hello, world!**。這並不是個實用的函式。要增加函式的彈性,例如打招呼的對象,我們可以增加新的**參數(parameter/argument)**。它提供額外的資料給函式使用。
|
|
|
|
|
|
|
|
|
|
參數會寫在定義函式的地方,以括號與逗號標記與分隔:
|
|
|
|
|
|
|
|
|
|
```javascript
|
|
|
|
|
function name(param, param2, param3) {
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
現在我們更新函式 `displayGreeting`,讓它支援打招呼的對象:
|
|
|
|
|
|
|
|
|
|
```javascript
|
|
|
|
|
function displayGreeting(name) {
|
|
|
|
|
const message = `Hello, ${name}!`;
|
|
|
|
|
console.log(message);
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
當我們要呼叫函式時,輸入需要的參數在括號中:
|
|
|
|
|
|
|
|
|
|
```javascript
|
|
|
|
|
displayGreeting('Christopher');
|
|
|
|
|
// 呼叫完,印出字串 "Hello, Christopher!"
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## 預設值(Default values)
|
|
|
|
|
|
|
|
|
|
我們利用參數增加了函式的彈性。但如果我們不想每次都要指定參數給函式使用呢? 繼續之前的例子,保留對象的名稱外,我們增加招呼語的種類。我們可以定義招呼語的預設值,若使用者沒有指定哪一種招呼語時,就使用預設值。它的方法就與賦予變數數值一樣 ── `parameterName = 'defaultValue'`。例如:
|
|
|
|
|
|
|
|
|
|
```javascript
|
|
|
|
|
function displayGreeting(name, salutation='Hello') {
|
|
|
|
|
console.log(`${salutation}, ${name}`);
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
當我們呼叫函式時,我們可以選擇是否要指定招呼語到 `salutation` 中。
|
|
|
|
|
|
|
|
|
|
```javascript
|
|
|
|
|
displayGreeting('Christopher');
|
|
|
|
|
// 輸出字串 "Hello, Christopher"
|
|
|
|
|
|
|
|
|
|
displayGreeting('Christopher', 'Hi');
|
|
|
|
|
// 輸出字串 "Hi, Christopher"
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## 回傳值(Return values)
|
|
|
|
|
|
|
|
|
|
目前為止,我們的函式只能輸出字串到[console](https://developer.mozilla.org/docs/Web/API/console)上。這或許是我們希望的結果,尤其是需要呼叫其他服務的時候。萬一今天我想建立一個額外的函式負責做資料處理與運算呢?
|
|
|
|
|
|
|
|
|
|
此時,我們可以利用**回傳值**。回傳值由函式輸出,就像變數一樣儲存像是字串或是數字的結果。
|
|
|
|
|
|
|
|
|
|
如果函式有定義回傳值,那就需要使用關鍵字 `return` 。關鍵字 `return` 需要附帶回傳的數值或是參考物件在後方,如:
|
|
|
|
|
|
|
|
|
|
```javascript
|
|
|
|
|
return myVariable;
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
我們建立一個函式專門建立招呼訊息並回傳給呼叫者:
|
|
|
|
|
|
|
|
|
|
```javascript
|
|
|
|
|
function createGreetingMessage(name) {
|
|
|
|
|
const message = `Hello, ${name}`;
|
|
|
|
|
return message;
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
當函式被呼叫時,變數會儲存函式回傳的數值。這就像我們給變數定值一樣: `const name = 'Christopher'`。
|
|
|
|
|
|
|
|
|
|
```javascript
|
|
|
|
|
const greetingMessage = createGreetingMessage('Christopher');
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## 將函式作為函式參數使用
|
|
|
|
|
|
|
|
|
|
在你的程式旅程中,你會見到有函式將其他函式當作參數使用。這個俐落的手法常被用在一種情況:我們不知道 A 事件什麼時候發生與完成,但我們要在 A 事件後執行 B 事件。
|
|
|
|
|
|
|
|
|
|
舉例來說,考慮函式[setTimeout](https://developer.mozilla.org/docs/Web/API/WindowOrWorkerGlobalScope/setTimeout),它會啟動計時機制,並在倒數完後執行下一個程式。我們需要告訴函式哪一個函式要在時間到後執行,一個完美的例子!
|
|
|
|
|
|
|
|
|
|
執行下方的程式,三秒鐘之後你會看到訊息**已經過三秒鐘**。
|
|
|
|
|
|
|
|
|
|
```javascript
|
|
|
|
|
function displayDone() {
|
|
|
|
|
console.log('已經過三秒鐘');
|
|
|
|
|
}
|
|
|
|
|
// 計時單位為毫秒。
|
|
|
|
|
setTimeout(displayDone, 3000);
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### 不記名函式(Anonymous functions)
|
|
|
|
|
|
|
|
|
|
回顧我們所建的函式,這些函式都只被執行了一次。當程式越來越複雜,我們可能建了許多的函式,但他們可能都只被呼叫了一次。這並不是理想的方式,那不如,不要給它函式名稱!
|
|
|
|
|
|
|
|
|
|
我們可以傳遞函式作為參數使用,也可以直接在參數裡建立新的函式。同樣使用關鍵字 `function`,但我們寫在參數欄當中。
|
|
|
|
|
|
|
|
|
|
試著以不記名函式的方式改寫程式碼:
|
|
|
|
|
|
|
|
|
|
```javascript
|
|
|
|
|
setTimeout(function() {
|
|
|
|
|
console.log('3 seconds has elapsed');
|
|
|
|
|
}, 3000);
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
執行上述程式後可以得到相同的結果。我們建立了一個函式,一個沒有名字的函式!
|
|
|
|
|
|
|
|
|
|
### 箭頭函式(Fat arrow functions)
|
|
|
|
|
|
|
|
|
|
許多程式語言,包含 JavaScript,都有一個常見的快捷語法稱作**箭頭(arrow/fat arrow)**函式。 它使用 `=>` 表示法,就像是箭頭一樣,如同它的名稱!使用 `=>` 可以省略關鍵字 `function`。
|
|
|
|
|
|
|
|
|
|
再一次改寫程式碼,這次我們使用箭頭函式:
|
|
|
|
|
|
|
|
|
|
```javascript
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
console.log('3 seconds has elapsed');
|
|
|
|
|
}, 3000);
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### 使用不同策略的時機
|
|
|
|
|
|
|
|
|
|
現在你已經學會了三種將函式作為參數的方法了。你可能會好奇使用它們的時機點為何。如果你知道你會重複使用一個函式,請使用正常的方法;如果你知道函式只用在特定的函式內一次,這就是用無記名函式的時機;箭頭函式與傳統 `function` 語法則是取決與你自己,但多數的開發者比較偏好使用 `=>`。
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## 🚀 挑戰
|
|
|
|
|
|
|
|
|
|
你能用一句話清楚地說明這些函式與方法的差別嗎? 試試看吧!
|
|
|
|
|
|
|
|
|
|
## 課後測驗
|
|
|
|
|
[課後測驗](https://ashy-river-0debb7803.1.azurestaticapps.net/quiz/10?loc=zh_tw)
|
|
|
|
|
|
|
|
|
|
## 複習與自學
|
|
|
|
|
|
|
|
|
|
這很值得去閱讀[關於箭頭函式的資料](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Functions/Arrow_functions),它們越來越常被用在程式碼上。試著寫個函式,再改寫成箭頭語法。
|
|
|
|
|
|
|
|
|
|
## 作業
|
|
|
|
|
|
|
|
|
|
[把玩函式](assignment.zh-tw.md)
|