← JavaScript Learning Path

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

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);
caller2, 3arguments→a=2, b=3parametersreturn 5

先分清楚「宣告 function」和「呼叫 function」。宣告描述可執行行為;call 才真的開始一次 execution。

3. Parameter vs Argument

Term出現位置例子
parameterfunction definitiona、b
argumentfunction call2、3
return valuefunction 完成後給 caller5

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);
Callnresultcaller receives
double(3)36a = 6
double(10)1020b = 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)  // 10

10. 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 內追

  1. caller 傳了哪些 arguments?
  2. parameters 進來後的 value/type?
  3. condition 走哪個 branch?
  4. 是否真的執行到 return?
  5. 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

18. Further reading

Knowledge check

  1. 解釋 parameter 與 argument 的差異。
  2. 說明 console.log 為何不能取代 return。
  3. 描述 local scope 與每次 call 的獨立 state。
  4. 指出 pure function 為何容易 test。
  5. 完成 clamp() mastery exercise 並通過所有 boundary tests。