# JavaScript 入門 - 函式與方法 ![JavaScript 入門 - 函式](../images/webdev101-js-functions.png) > 由 [Tomomi Imura](https://twitter.com/girlie_mac) 繪製 ## 課前測驗 [課前測驗](https://nice-beach-0fe9e9d0f.azurestaticapps.net/quiz/9) 撰寫程式碼時,我們必須確保程式碼的閱讀性。聽來不太直覺,理解程式碼的時間遠比撰寫時間來的久。裡面最需要被管理的程式項目就是**函式**。 [![函式與方法](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/en-US/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/en-US/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://nice-beach-0fe9e9d0f.azurestaticapps.net/quiz/10) ## 複習與自學 這很值得去閱讀[關於箭頭函式的資料](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions),它們越來越常被用在程式碼上。試著寫個函式,再改寫成箭頭語法。 ## 課題 [把玩函式](assignment.zh-tw.md)