核心想法:一台你能命名的機器
想像你桌上放著一台小小的自動販賣機。你投進幾枚硬幣、按下一個按鈕,零食就掉出來了。你不會每次餓了都重造一台機器——你只是再用它一次。函式就是這樣一台機器,只不過是用程式碼做的:你給它一些輸入,它做一件工作,再把一個結果遞還給你。
這裡的關鍵詞是重複使用。你把機器造出來一次,給它取個清楚的名字,比如 add、greet 或 sendEmail——從此以後,你想執行它多少次都行,從程式的任何地方,只要叫出它的名字就可以。寫一次,處處用。
解剖:函式的四個部件
每個函式都由同樣的四個部件搭成,一旦你能認出它們,就能讀懂任何語言裡的任何函式。它們是:名字(以後你用什麼來叫它)、參數(輸入塞進去的那些槽位)、函式主體(它實際做的工作),以及回傳值(它遞還給你的結果)。這裡有一個完整的小例子:
function greet(name) {
return "Hello, " + name + "!";
}
greet("Mia"); // -> "Hello, Mia!"把它當成一句話來讀。function 這個詞在宣告:「我要造一台機器了。」greet 是機器外殼上寫的名字。括號裡的 (name) 是輸入的槽位。大括號裡的一切是函式主體——也就是那件工作。而 return 就是機器把結果放進你手心的那一刻。整個結構就這麼簡單。
為什麼它們重要:寫一次,改一次
設想一個程式,它在十二個不同的地方向使用者打招呼,每一次你都把同樣那三行拼問候語的程式碼複製貼上了一遍。這種複製貼上出來的填充物有個名字:樣板程式碼(boilerplate),就是那些反覆出現、把程式碼塞得亂糟糟、卻不帶來任何新意的「管線工程」。十二份副本,意味著十二次打錯字的機會,和十二個要記著的地方。
現在設想同一個程式,只有一個 greet 函式,被呼叫了十二次。邏輯只存在於唯一的一個地方。等哪天你決定問候語該說「Welcome」而不是「Hello」,你只改這一處——那十二個呼叫它的地方就全都免費跟著更新了。這就是函式那個安靜的超能力:只在一個地方修 bug。
這也是為什麼函式讓重構(refactor)——也就是在不改變功能的前提下整理、重塑你的程式碼——變得輕鬆得多。當每一件工作都封裝在它自己那台有名字的機器裡時,你就可以給它改名、讓它跑得更快、或者替換它的內部,全程都不必碰程式的其他部分。整齊的盒子,最好挪動。
呼叫 vs 定義:先造出來,再按按鈕
一個函式的「一生」裡有兩個截然不同的時刻,新手常把它們混為一談。定義一個函式,是在造機器——把名字、槽位、函式主體都寫出來。這時候其實什麼都還沒發生;你只是把它裝配好,擱到架子上。呼叫一個函式,是按下它的按鈕——告訴它「上,現在就去做事,輸入在這裡」。
當你呼叫它時,你遞過去的值會填進那些參數槽位。在函式內部,這些輸入就像一批嶄新的變數(variables)——一個個有名字的盒子,機器在執行時可以讀取它們、用它們做事。傳進 "Mia",那個叫 name 的盒子在這一趟工作裡就裝著 "Mia"。再用 "Sam" 呼叫一次,同一台機器這回就改用 "Sam" 來做事了。
function add(a, b) { // define it once
return a + b;
}
add(2, 3); // call it -> 5
add(10, 7); // call it again -> 17小結:要帶走的那一個想法
函式是你作為程式設計師將拉動的第一根真正的槓桿,你日後造的幾乎一切,都是由它們搭起來的。整個想法一口氣就能說完:給一件工作取個名字、餵給它輸入、拿到結果、然後永遠重複使用它。掌握了這一點,你的程式碼就不再是一長條往下拉的捲軸,而開始變成一間工作坊,裡頭擺滿了整齊、貼好標籤的小機器。
- 定義一次:寫出名字、參數槽位、函式主體,以及一個回傳值。
- 想呼叫多少次就多少次:說出名字,括號裡放上真實的輸入。
- 把輸入傳進去:它們會填進參數,在函式內部就像嶄新的變數一樣。
- 雙重收穫:現在少寫重複的樣板程式碼,以後修改或重構只需動一處。