白帶 · 程式思維
第 04 章

函式 —
把魔法打包

這章結束你會把重複步驟打包成函式,讀懂輸入、輸出與呼叫的關係。

開場 · 小機器的想像

你有沒有發現,很多店員不是每次都重新想一遍流程?點一杯珍珠奶茶,店員不會從「什麼是杯子」開始思考。他按既定步驟:拿杯、加料、倒茶、封膜、貼標。這一整包動作有名字,叫做一杯珍奶。程式裡的函式也一樣:把一組步驟包起來,取一個名字,需要時叫它出來做事。對完全不會寫程式的人來說,函式是第一個真正讓 code 變短、變清楚、變能改的工具。你不是在背語法,而是在學怎麼把混亂的工作分裝成一台台可靠的小機器。

為什麼要打包:同樣的事寫三遍很蠢

先想像你在經營一個小攤位。每賣一杯飲料,都要在原價上加十元服務費。如果今天只賣一杯,你直接算 price + 10 沒問題。可是賣三杯、三十杯、三百杯時,難道每個地方都手寫一次「加十元」嗎?這樣看起來只是麻煩,真正可怕的是:哪天服務費改成十五元,你要去所有地方慢慢找,漏改一個,帳就錯了。

函式(function)的第一個價值,就是把一組會重複出現的步驟打包。你把「加服務費」包成一台小機器,以後只要把價格丟進去,它就吐出加完費用的結果。這跟 ch01 學過的食譜很像:食譜是一串清楚步驟,函式是把那串步驟裝進一個有名字的盒子。需要做菜時,不用重寫整本食譜,只要說「做這道菜」。

打包還有一個好處:錯誤比較集中。若加服務費的規則寫在十個地方,錯誤可能藏在十個角落;若規則放在同一個函式,你只要檢查那台機器。這讓修改變得可控,也讓測試更有方向。

函式也讓 code 比較像人話。看到 addServiceFee(80),你大概猜得出它在做什麼;看到一堆散落在各處的 80 + 10,你就要自己猜那個十元代表什麼。對初學者來說,這點很重要。你不需要一次看懂所有細節,但你可以先看懂名字,再決定要不要打開那個函式檢查裡面的步驟。

這也是未來和 AI 合作時的基本功。AI 可能一次產出很多 code,如果它把每件事都攪在一起,你會很難修改;如果它把工作切成有名字的小函式,你就能一段一段檢查。函式不是炫技,它是把混亂收進抽屜,讓你知道每個抽屜負責哪件事。

把加服務費打包成函式後,三個價格都能用同一組步驟處理。
function addServiceFee(price) {
  return price + 10;
}

console.log(addServiceFee(80));
console.log(addServiceFee(120));
console.log(addServiceFee(50));

函式解剖:名字、參數、回傳值

一個函式通常有三個你要先認出的部分:名字、參數、回傳值。名字就是這台機器的招牌,例如 makeJuice。參數(parameter)是你丟進機器的材料,例如水果和杯數。回傳值(return value)是機器做完後端出來的結果,例如「2 杯芒果汁」。

把它想成果汁機會很直覺。果汁機本身有名字,可能叫「打果汁」。你放進芒果、牛奶、冰塊,這些就是輸入。按下按鈕後,果汁機不會只在裡面忙一陣子就消失,它會把一杯果汁交給你,這就是輸出。程式裡的 return 就像「把成品端出來」。如果沒有回傳值,函式可能有做事,但外面的人拿不到成品。

很多初學者會把參數和變數混在一起。你可以這樣分:參數是函式門口的收件欄,呼叫函式時放進去;函式裡面可以拿這些材料做事。呼叫 makeJuice("芒果", 2) 時,fruit 收到「芒果」,cups 收到 2。下一次呼叫 makeJuice("蘋果", 1),同一台機器改用不同材料,做出不同結果。

這就是函式最迷人的地方:步驟固定,材料可換。就像同一份早餐食譜可以做給不同人吃,份量和口味不同,但流程不用重寫。你在讀 AI 寫的 code 時,先找函式名字,再看括號裡收什麼參數,最後找 return 端出什麼。這三個點連起來,函式大意通常就跑不掉。

同一個函式換不同參數,就能做出不同的回傳值。
function makeJuice(fruit, cups) {
  const result = cups + " 杯" + fruit + "汁";
  return result;
}

console.log(makeJuice("芒果", 2));
console.log(makeJuice("蘋果", 1));

呼叫函式:按下果汁機的按鈕

寫好函式還不代表它已經執行。函式像一台放在桌上的果汁機,你把它買回來、插好電,它仍然不會自己轉。你必須按下按鈕。程式裡「按下按鈕」的動作叫呼叫(call)。看到 double(21) 這種名字後面接括號的寫法,通常就是在呼叫函式。

呼叫時,括號裡放進去的值會送給參數。函式跑完後,如果有 return,整個呼叫會變成那個回傳值。這句話一開始有點抽象,你可以把 double(21) 心裡替換成「42」。因為函式收到 21,做了乘以 2,回傳 42,所以外面的程式可以把它存進變數,也可以再交給下一個函式。

這裡有一個常見誤會:returnconsole.log 不是同一件事。console.log 是把東西印給人看,像把便條紙貼在螢幕上;return 是把結果交回程式,讓後面的步驟能繼續使用。很多初學者只看到畫面印出東西,就以為函式有把值交出去。實際上,如果沒有 return,外面通常拿不到可繼續加工的結果。

判斷一段函式有沒有真的交出結果,可以看呼叫後面接著發生什麼事。如果程式寫成 const total = addTax(50),代表它期待函式回傳一個值,才能放進 total 這個盒子。如果只是單獨寫 addTax(50),可能是在觸發某個動作,也可能是作者忘了把結果接住。這個觀察習慣會讓你讀 code 時更穩,不會只被畫面輸出牽著走。

當你請 AI 寫函式時,可以很具體地說:「這個函式要接收什麼參數?要回傳什麼?不要只印在畫面上。」這句話會讓 AI 更容易產出能組合的 code。純 vibe coder 只看畫面有沒有東西,你會進一步確認那個結果能不能被下一步使用。

函式呼叫可以變成一個值,這個值還能繼續交給下一次呼叫。
function double(number) {
  return number * 2;
}

const first = double(21);
const second = double(first);

console.log(first);
console.log(second);

拆解思維:把做早餐拆成小函式

如果你跟電腦說「幫我做早餐」,這句話太大了。電腦不知道早餐包含什麼,也不知道先做哪一步。這呼應 ch01 的重點:電腦快但笨,指令必須明確。函式讓你把大任務拆成幾個小任務,例如煎蛋、烤吐司、泡咖啡。每個小任務各自變成一個函式,最後再由 makeBreakfast 把它們組合起來。

這種拆解不是只有工程師才用。你整理房間時,也不會真的執行一個神祕的「整理」動作。你會先收垃圾、折衣服、擦桌面、把書放回架上。每一步都有明確結果。程式裡也是如此:大函式負責安排流程,小函式負責做好一件小事。讀 code 時,你可以先看大函式的流程,再決定要不要鑽進小函式看細節。

好的函式通常有清楚邊界。fryEgg 就負責煎蛋,不要順便結帳、寄信、改名字。邊界清楚,改某一段時比較不會撞壞別段。這點在 AI 產出的 code 裡尤其重要。AI 很容易為了快速完成,把顯示畫面、計算金額、儲存資料全部塞在同一坨。結果你只是想改稅率,卻不小心影響按鈕文字。

拆解時也要注意順序。早餐流程如果先把咖啡端出去,再發現吐司還沒烤,客人會等;程式流程如果先顯示總價,再去計算稅金,畫面就會出現舊結果。函式不是把步驟切開就結束,還要安排誰先誰後。這會把 ch03 學過的流程追蹤接回來:你看的是資料如何一站一站往下走。

拆解思維的實用問法是:「這件事能不能拆成三到五個有名字的小動作?」如果可以,就把每個小動作變成函式。名字要像任務,不要像謎語。calculateTotaldoThing 好,因為前者告訴你它在算總價。函式名越清楚,你越能不用逐字讀完,也能抓到整體流程。

大任務可以由幾個小函式組成,流程會比一大坨 code 更好讀。
function fryEgg() {
  return "煎蛋";
}

function toastBread() {
  return "烤吐司";
}

function makeDrink(drink) {
  return drink;
}

function makeBreakfast(drink) {
  const plate = [fryEgg(), toastBread(), makeDrink(drink)];
  return plate.join(" + ");
}

console.log(makeBreakfast("熱咖啡"));

內建函式:別人打包好送你的工具

不是每個函式都要自己寫。程式語言和瀏覽器已經準備很多內建函式(built-in function),像是 Math.round 可以四捨五入,alert 可以跳出提示。它們就像別人已經打包好的機器,你只要知道怎麼丟材料、會得到什麼結果,就能使用。

這件事很像站在巨人肩膀上。你不用自己打造四捨五入的機器,也不用從零研究瀏覽器怎麼跳提示。你只要查到工具的名字、需要的參數、回傳值或效果,就能把它放進自己的流程。這也是為什麼程式世界會發展得很快:大家把常用步驟打包,下一個人直接組合。

使用內建函式時,初學者要養成兩個檢查習慣。第一,確認它要吃什麼資料。Math.round 吃的是數字,如果你丟一段奇怪文字,它不會變魔法。第二,確認它會回傳值,還是只做效果。Math.round(99.6) 會回傳 100,所以可以存起來;alert("完成") 主要是讓畫面跳提示,通常不是拿來算下一步。

讀 AI code 時,看到不認識的函式不用慌。先分辨它是自己專案裡定義的函式,還是語言或瀏覽器提供的內建函式。接著問三件事:名字像在做什麼?括號裡放了什麼?結果被拿去哪裡?這三問足夠讓你在不會背全部語法的情況下,判斷那段 code 大概是否合理。

內建函式 Math.round 已經打包好四捨五入的步驟,可以放進自己的函式裡。
const rawTotal = 99.6;
const roundedTotal = Math.round(rawTotal);

function priceWithTax(price) {
  return Math.round(price * 1.1);
}

console.log(roundedTotal);
console.log(priceWithTax(50));
招牌互動 · function-factory

函式工廠:把數字送進小機器

拖曳機器排出管線,把輸入數字加工成指定輸出。也可以直接點機器加入管線;按「執行」後看每台函式如何接住上一台的回傳值。

機器庫

管線

輸出紀錄

  1. 執行後會列出每一步輸出。
✓ 五關全破 函式就是可重複使用的小機器;參數是丟進去的材料,回傳值是吐出來的結果。+30 XP 入袋。

會看函式邊界的人,修改 AI code 時會先找負責那件事的函式,而不是整頁亂改。你會檢查函式吃什麼參數、回傳什麼值,再判斷它能不能接到下一步。當 AI 把太多事情塞進一個函式,你會要求它拆成計算、顯示、儲存三種小函式。這讓你改 A 的時候,比較不會弄壞 B。

隨堂測驗 · 5 題

檢查一下這章

下一章預告

下一章你會真的在頁面上跑第一行 code,把今天的函式觀念變成可操作的輸出。