JAVASCRIPT V2 · FOUNDATIONS · 39
Functions 與 Scope:把一段行為變成有 Contract 的單位
Function 不是「把很多行包起來」而已。更重要的是它建立了一個 interface:caller 提供 arguments,function 透過 parameters 取得資料,在自己的 scope 執行,最後 return result 或產生 side effect。這個模型會一路延伸到 event handler、API route、React component 與 testable business logic。
Learning outcomes
- 區分 declaration、call、parameter、argument、return value。
- 知道
console.log與return完全不同。 - 理解 local scope、outer scope 與每次 call 的獨立 local state。
- 能設計小型 pure function contract。
- 能從 caller → arguments → branch → return 追蹤 function bug。
1. Why now:Loop 能重複處理,但程式還缺少「命名行為」
上一課的 Grade Tracker 已經有計算平均、判斷及格等邏輯。如果所有 code 都留在同一區塊,很快就會難以重用與測試。
function isPassed(score) {
return score >= 60;
}
現在「及格規則」變成一個有名字、有 input、有 output 的 unit。
2. Function call 的完整資料流
function add(a, b) {
const result = a + b;
return result;
}
const total = add(2, 3);
先分清楚「宣告 function」和「呼叫 function」。宣告描述可執行行為;call 才真的開始一次 execution。
3. Parameter vs Argument
| Term | 出現位置 | 例子 |
|---|---|---|
| parameter | function definition | a、b |
| argument | function call | 2、3 |
| return value | function 完成後給 caller | 5 |
4. Worked Example A:return 不是 console.log
function add(a, b) {
console.log(a + b);
}
const result = add(2, 3);
console.log(result); // undefined
第一個 log 是 side effect;function 沒有 return value,因此 caller 取得 undefined。
function add(a, b) {
return a + b;
}
const result = add(2, 3); // 5
如果 caller 後面還需要這個資料,就必須設計 return contract。
5. return 也會結束目前 function call
function grade(score) {
if (score >= 60) {
return "Pass";
}
return "Fail";
console.log("never runs");
}
一旦執行到 return,這次 call 就完成;後面的 statement 不再執行。
6. Scope:不是每個名稱到處都能看見
const taxRate = 0.05;
function total(price) {
const fee = price * taxRate;
return price + fee;
}
console.log(fee); // ReferenceError
fee 是 local binding,只存在 function scope 內。taxRate 在 outer scope,因此 function body 可以讀。
7. Block scope:let / const 也受大括號限制
if (true) {
const message = "inside";
}
console.log(message); // ReferenceError
建立 scope boundary 是大型程式避免名稱彼此污染的重要機制。
8. Worked Example B:每次 call 都有自己的 local state
function double(n) {
const result = n * 2;
return result;
}
const a = double(3);
const b = double(10);
| Call | n | result | caller receives |
|---|---|---|---|
double(3) | 3 | 6 | a = 6 |
double(10) | 10 | 20 | b = 20 |
先把它理解成「每次 call 有自己的 local execution state」。77 Memory / Stack / Heap 會再深入。
9. Pure function:最容易推理與測試的起點
function clamp(value, min, max) {
if (value < min) return min;
if (value > max) return max;
return value;
}
同樣 input 得到同樣 output,而且不偷偷修改外部 state。這種 function 非常適合 unit test。
clamp(5, 0, 10) // 5
clamp(-3, 0, 10) // 0
clamp(99, 0, 10) // 1010. Side effect 不等於壞;重點是要知道它存在
function showMessage(text) {
document.querySelector("#status").textContent = text;
}
這個 function 修改 DOM,因此有 side effect。Browser programming 需要 side effects;只是 business calculation 與 side effect 分開,通常更容易測。
11. Worked Example C:Function composition
function isPassed(score) {
return score >= 60;
}
function resultLabel(score) {
return isPassed(score) ? "Pass" : "Fail";
}
小 function 可以互相組合。重要的是每個 contract 都清楚,而不是把所有事情塞進一個 100 行 function。
12. Common mistakes
A. 忘記 return
console 有答案,但 caller 收到 undefined。
B. parameter name 與 outer state 混淆
let score = 90;
function addBonus(score) {
score += 5;
return score;
}
這裡 local parameter score 與 outer binding 同名;它們不是同一個 binding。
C. 一個 function 同時做太多事
讀 input、驗證、計算、改 DOM、fetch、storage 全塞一起會讓 test 與 debug 困難。
D. 以為 declaration 就已經執行
function definition 出現不代表 body 已跑;要看到 call。
13. Debug evidence:從 caller 往 function 內追
- caller 傳了哪些 arguments?
- parameters 進來後的 value/type?
- condition 走哪個 branch?
- 是否真的執行到 return?
- caller 收到什麼 return value?
console.log({ value, min, max });
const result = clamp(value, min, max);
console.log({ result });14. Guided exercise:預測 return
function mystery(n) {
if (n < 0) return 0;
return n * 2;
}
const a = mystery(-3);
const b = mystery(4);
逐次 call 寫出 argument、branch、return、caller binding。
15. Independent exercise:實作 clamp(value,min,max)
至少測:
clamp(5, 0, 10) // 5
clamp(-3, 0, 10) // 0
clamp(99, 0, 10) // 10
clamp(7, 7, 7) // 7
最後一筆是 boundary:min 和 max 相同。
16. Challenge:把 calculation 與 output 分開
設計兩個 function:
calculateAverage(scores) // 只計算
renderAverage(value) // 只更新畫面
想想這樣比一個 function 同時計算與 DOM update 更容易在哪裡測試。
Project checkpoint:Grade Tracker 的 Business Functions
function isPassed(score) {
return score >= 60;
}
function gradeLabel(score) {
if (score >= 90) return "A";
if (score >= 80) return "B";
if (score >= 60) return "Pass";
return "Fail";
}
function average(scores) {
let total = 0;
for (const score of scores) total += score;
return scores.length ? total / scores.length : 0;
}
現在 project 已有可以單獨測試的 business units。下一課才正式建立多筆學生資料模型。
17. Vocabulary / Summary
- declaration:定義 function。
- call/invocation:實際執行一次 function。
- parameter:definition 內接收 input 的名稱。
- argument:caller 傳入的具體 value。
- return value:function 交還給 caller 的資料。
- scope:binding 可被存取的範圍。
- side effect:除了 return value 之外對外部世界造成的可觀察改變。
18. Further reading
Knowledge check
- 解釋 parameter 與 argument 的差異。
- 說明
console.log為何不能取代 return。 - 描述 local scope 與每次 call 的獨立 state。
- 指出 pure function 為何容易 test。
- 完成
clamp()mastery exercise 並通過所有 boundary tests。