Lesson 0.5.1 — Function Declaration
0. Metadata
| Field | Value |
|---|---|
| Stage | 0 — JavaScript Language Foundation |
| Module | 0.5 — Functions |
| Lesson | 0.5.1 |
| Competency | C01.5 — Functions |
| Depth Target | L2–L3 |
| Prerequisites | Values & Variables (0.2), Control Flow (0.4) |
| Estimated Cognitive Load | Medium |
1. Why This Exists (Vì sao cần học)
Bạn đang viết một script tính tổng tiền cho 3 đơn hàng:
const total1 = price1 * quantity1 * 1.1;
const total2 = price2 * quantity2 * 1.1;
const total3 = price3 * quantity3 * 1.1;Công thức giống hệt nhau. Nếu thuế đổi từ 10% sang 8%, bạn phải sửa 3 chỗ. Nếu quên một chỗ, bug xuất hiện.
Vấn đề cốt lõi
Code lặp lại không chỉ dài mà còn dễ hỏng. Một function declaration giải quyết điều này bằng cách đóng gói logic thành một đơn vị có tên, tái sử dụng được, và sửa một chỗ áp dụng mọi nơi.
Đây không chỉ là syntax. Đây là bước đầu tiên để bạn viết code từ kiểu "viết tuần tự" sang "thiết kế logic có cấu trúc".
2. Prerequisites (Yêu cầu đầu vào)
Trước khi học bài này, bạn cần:
- Biết khai báo biến bằng
const/letvà hiểu binding (0.2.2). - Biết các kiểu dữ liệu cơ bản: string, number, boolean, undefined (0.2.3).
- Biết
ifvà block scope ở mức sử dụng (0.4.2). - Biết
console.logđể quan sát output.
WARNING
Nếu bạn chưa chắc const chỉ cấm reassignment chứ không cấm mutation, quay lại 0.2.2 trước.
3. Learning Objectives (Mục tiêu học tập)
Sau bài này, bạn có thể:
- Viết một function declaration đúng cú pháp với tên, tham số, và body.
- Phân biệt parameter (tên trong khai báo) và argument (giá trị khi gọi).
- Dự đoán giá trị trả về khi có hoặc không có
return. - Phân biệt side effect (làm thay đổi bên ngoài) và return value (xuất dữ liệu).
- Trace luồng dữ liệu từ argument → parameter → body → return → caller.
4. Mental Model (Mô hình tư duy)
Hãy hình dung một function declaration như một công thức nấu ăn có tên:
Tên công thức: calculateTax
Nguyên liệu: price, rate
Các bước: price * rate
Kết quả: trả về số tiền thuếKhi bạn khai báo (declaration), bạn chỉ đang viết công thức vào sổ — chưa nấu. Khi bạn gọi (call), bạn mang nguyên liệu thật đến và chạy công thức.
Mental Model
Function Declaration
↓
[Name] + [Parameters] + [Body]
↓
Call with Arguments
↓
Parameters bound to argument values
↓
Body executes line by line
↓
Return statement → value goes back to caller
↓
No return → undefined goes back to callerQuan trọng: return là cửa ra duy nhất để đưa giá trị từ bên trong function ra ngoài. console.log chỉ in ra màn hình, không đưa giá trị đi đâu cả.
5. Core Concepts (Các khái niệm cốt lõi)
Essential (Bắt buộc)
| Concept | Ý nghĩa |
|---|---|
| Function Declaration | function name(params) { body } — tạo một đơn vị logic có tên. |
| Parameter | Tên đại diện trong khai báo, nhận giá trị khi function được gọi. |
| Argument | Giá trị thực tế truyền vào khi gọi function. |
| Return | Đưa giá trị ra khỏi function và dừng thực thi body. |
| Side Effect | Bất kỳ thay đổi nào bên ngoài function (log, mutate global, DOM, v.v.). |
Supporting (Hỗ trợ)
| Concept | Ý nghĩa |
|---|---|
| Early Return | Dùng return bên trong if để thoát sớm, giảm nesting. |
| Function Naming | Dùng động từ + đối tượng: calculateTotal, isValid, formatName. |
Awareness (Biết tồn tại)
| Concept | Lý do chưa đào sâu |
|---|---|
| Function Hoisting | Trong mental model sơ cấp, ta tạm hình dung engine "nhận diện" declaration trước khi thực thi. Cơ chế chính xác thuộc Stage 1. |
| Function as Object | Function thực chất là object đặc biệt, nhưng sẽ học ở Stage 2. |
Out of Scope (Không thuộc bài này)
- Function Expression (0.5.2)
- Arrow Function (0.5.3)
- Default Parameters (0.5.4)
- Rest Parameters (0.5.4)
this(Stage 2)- Closure (Stage 1)
- Execution Context (Stage 1)
6. Worked Example (Ví dụ phân tích từng bước)
Xét đoạn code sau:
function calculateTotal(price, quantity) {
const subtotal = price * quantity;
const tax = subtotal * 0.1;
return subtotal + tax;
}
const bill = calculateTotal(100, 2);
console.log(bill);Walkthrough
Step 1 — Declarationfunction calculateTotal(price, quantity) tạo một đơn vị logic tên calculateTotal. price và quantity là parameters — chúng chưa có giá trị, chỉ là chỗ để nhận giá trị sau này.
Step 2 — CallcalculateTotal(100, 2) là lệnh gọi. 100 và 2 là arguments.
priceđược bound với100quantityđược bound với2
Step 3 — Execution Body chạy từ trên xuống:
subtotal = 100 * 2→200tax = 200 * 0.1→20
Step 4 — Returnreturn subtotal + tax → return 220. Function dừng lại. Giá trị 220 được đưa ra ngoài.
Step 5 — Assignmentconst bill = 220. Biến bill giờ giữ giá trị 220.
Step 6 — Side Effectconsole.log(bill) in ra 220. Đây là side effect — nó làm thay đổi màn hình console, nhưng không liên quan đến return value của calculateTotal.
7. Prediction Exercise (Bài tập dự đoán)
Đừng chạy code. Đọc và dự đoán output, sau đó giải thích tại sao.
Câu 1
function greet(name) {
const message = "Hello " + name;
}
console.log(greet("An"));Câu 2
function checkAge(age) {
if (age < 18) return "minor";
return "adult";
console.log("done");
}
console.log(checkAge(15));Câu 3
function multiply(a, b) {
return a * b;
}
console.log(multiply(5));Câu 4
function describeOrder(item, qty, price) {
return `${qty}x ${item} = ${price * qty}`;
}
console.log(describeOrder("Coffee", 2, 25));[Đáp án & Giải thích]
Câu 1: undefined
- Giải thích:
greettạo ramessagenhưng khôngreturnnó. Khi function không córeturn, JavaScript tự động returnundefined.
Câu 2: "minor"
- Giải thích:
age < 18đúng nênreturn "minor"chạy. Dòngreturn "adult"bị bỏ qua.console.log("done")không bao giờ chạy vìreturnđã thoát function.
Câu 3: NaN
- Giải thích: Chỉ truyền một argument
5, nêna = 5,b = undefined.5 * undefined→NaN. Không có lỗi thrown; JavaScript gánundefinedcho parameter thiếu.
Câu 4: "2x Coffee = 50"
- Giải thích:
- Argument
"Coffee"→ parameteritem - Argument
2→ parameterqty - Argument
25→ parameterprice qty(2) ×price(25) = 50. Template literal trả về"2x Coffee = 50".
- Argument
8. Implementation Lab (Bài lab thực hành)
Level 1 — Guided (Có hướng dẫn)
Viết một function tính chỉ số BMI:
// Tên: calculateBMI
// Parameters: weight (kg), height (m)
// Formula: weight / (height * height)
// Return: số BMI, làm tròn 1 chữ số thập phân
function calculateBMI(weight, height) {
// implement here
}[Đáp án tham khảo]
function calculateBMI(weight, height) {
const bmi = weight / (height * height);
return Math.round(bmi * 10) / 10;
}
console.log(calculateBMI(70, 1.75));
// 22.9Giải thích:
- Tính BMI theo công thức
weight / (height * height). Math.round(bmi * 10) / 10giúp làm tròn kết quả đến 1 chữ số thập phân.
Level 2 — Partial Scaffold (Khung sẵn)
Hoàn thành function kiểm tra email đơn giản (chỉ kiểm tra có chứa @ và .):
function isValidEmail(email) {
// return true nếu email chứa '@' và '.'
// ngược lại return false
}[Đáp án tham khảo]
function isValidEmail(email) {
return email.includes("@") && email.includes(".");
}
console.log(isValidEmail("user@example.com"));
// true
console.log(isValidEmail("userexample.com"));
// falseGiải thích:
includes("@")kiểm tra chuỗi có chứa@includes(".")kiểm tra chuỗi có chứa dấu.
Toán tử && yêu cầu cả hai điều kiện đều đúng.
Đây chỉ là cách kiểm tra email đơn giản theo yêu cầu bài lab, chưa phải validation email đầy đủ.
Level 3 — Independent (Tự viết)
Viết function formatPrice nhận vào amount (number) và currency (string). Trả về chuỗi định dạng: "1,000,000 VND".
Gợi ý
Dùng amount.toLocaleString() và nối chuỗi. Không dùng console.log bên trong function — hãy return chuỗi đã format.
[Đáp án tham khảo]
function formatPrice(amount, currency) {
return `${amount.toLocaleString()} ${currency}`;
}
console.log(formatPrice(1000000, "VND"));
// "1,000,000 VND"
console.log(formatPrice(250000, "USD"));
// "250,000 USD"Giải thích:
amount.toLocaleString() chuyển số thành chuỗi có dấu phân cách hàng nghìn.
Sau đó dùng template literal để nối số đã format với currency: ${amount.toLocaleString()} ${currency}
9. Edge Cases (Các trường hợp ngoại lệ)
Thiếu argument
function add(a, b) {
return a + b;
}
add(5); // NaN, vì b === undefinedTại sao: JavaScript không bắt buộc số argument khớp số parameter. Parameter thiếu nhận undefined.
Cách nhận biết: Kết quả là NaN hoặc unexpected behavior khi toán tử gặp undefined.
Cách xử lý: Validation bên trong function (sẽ học sâu ở 0.7.4).
Thừa argument
function square(x) {
return x * x;
}
square(3, 4, 5); // 9Tại sao: 4 và 5 bị bỏ qua. Không lỗi, không crash.
Cách nhận biết: Code vẫn chạy nhưng có thể gây hiểu nhầm về ý định (intent).
Cách xử lý: Đảm bảo tên function và parameter rõ ràng để caller không truyền thừa.
Return rỗng
function demo() {
return;
}Tại sao: return không có expression tương đương return undefined.
Cách nhận biết: Caller nhận undefined dù function có vẻ "đã return".
Code sau return không chạy
function test() {
return 1;
console.log("never");
}Tại sao: return là điểm thoát. Dòng sau là unreachable code.
Cách nhận biết: Linter hoặc engine cảnh báo. Trong production, đây là dead code cần xóa.
10. Debug Lab (Bài lab gỡ lỗi)
Symptom (Triệu chứng): Function double trả về undefined dù nhìn có vẻ đúng.
function double(x) {
console.log(x * 2);
}
const result = double(5);
console.log("Result is:", result); // "Result is: undefined"Reproduction (Tái hiện lỗi): Chạy code trên. Quan sát console in 10 nhưng result là undefined.
Evidence (Bằng chứng):
console.log(x * 2)in ra10→ body chạy đúng.resultnhậnundefined→ không có giá trị nào ra khỏi function.
Hypothesis (Giả thuyết): Developer nhầm lẫn console.log (side effect) với return (xuất giá trị).
Verification (Xác minh): Thay console.log(x * 2) bằng return x * 2;. Chạy lại — result giờ là 10.
Root Cause (Nguyên nhân gốc rễ): Thiếu return. console.log chỉ gửi dữ liệu đến console, không đưa dữ liệu về caller.
Fix (Sửa):
function double(x) {
return x * 2;
}Prevention (Phòng ngừa):
- Khi viết function, hỏi: "Caller cần nhận lại gì?" → dùng
return. - Khi debug, nếu variable nhận
undefinedtừ function call, kiểm trareturnđầu tiên.
11. Design Exercise (Bài tập thiết kế giải pháp)
INFO
Ở depth L2–L3, design exercise ở dạng Decision (lựa chọn) thay vì architecture phức tạp.
Bạn cần viết một function logUser nhận vào user object và in thông tin ra console. Đồng thời, team khác cần dùng function này để lấy chuỗi thông tin để gửi API.
function logUser(user) {
console.log(`User: ${user.name}, Age: ${user.age}`);
}function formatUser(user) {
return `User: ${user.name}, Age: ${user.age}`;
}Câu hỏi:
- Nếu team API dùng Option A, điều gì xảy ra?
- Option B có nhược điểm gì nếu bạn chỉ muốn in ra console?
- Bạn chọn cách nào để phục vụ cả hai nhu cầu?
[Đáp án tham khảo]
Giải thích:
- Team API gọi
logUser(user)nhận vềundefined, không có chuỗi để gửi đi. - Option B không tự in ra console; caller phải tự
console.log(formatUser(user)). - Chọn Option B (return) vì nó linh hoạt hơn. Caller muốn in thì
console.log(formatUser(u)), muốn gửi API thì dùng trực tiếp return value.
- Team API gọi
Kết luận: Ưu tiên
returncho utility functions. Side effect chỉ dùng khi mục đích duy nhất là tác động bên ngoài (ví dụ: ghi log hệ thống, cập nhật DOM).
12. Production Scenario (Tình huống thực tế)
Context: Bạn đang maintain một module tính toán giỏ hàng trong e-commerce.
function calculateDiscount(price, coupon) {
if (coupon === "SALE20") {
console.log(price * 0.8);
}
return price;
}Một junior developer thêm console.log để debug và quên xóa. Code được merge.
Symptom: Giá hiển thị trên UI đúng (price), nhưng tổng tiền sau giảm giá bị sai. Khi kiểm tra network tab, API vẫn nhận giá gốc.
Câu hỏi:
- Tại sao console in ra giá đúng nhưng API nhận giá sai?
- Sửa function như thế nào?
[Đáp án tham khảo]
Bạn nghĩ:
console.log(price * 0.8)chỉ in ra màn hình, không return giá trị. Dòngreturn priceluôn chạy, trả về giá gốc.- Sửa thành:js
function calculateDiscount(price, coupon) { if (coupon === "SALE20") { return price * 0.8; } return price; }
Bài học:
console.loglà công cụ debug tạm thời. Không bao giờ dùng nó thay thếreturntrong business logic.
13. AI-assisted Exercise (Bài tập với AI)
Level A — Ask
- Tự trả lời trước: Viết ra giấy sự khác biệt giữa
returnvàconsole.logtrong 2 câu. - Hỏi AI: "Explain the difference between
returnandconsole.login JavaScript functions." - So sánh câu trả lời AI với câu trả lời của bạn. AI có dùng từ "output" một cách mơ hồ không?
- Verify bằng MDN: tìm trang
returnvàconsole.logtrên MDN Web Docs.
Gợi ý
Để ý xem AI có nói "both output values" hay không. Nếu có, đó là điểm mơ hồ cần chỉnh. return xuất giá trị cho caller; console.log xuất giá trị cho console — hai "output" hoàn toàn khác layer.
Đáp án tham khảo
Bạn nghĩ:
returnđưa giá trị ra khỏi function để code khác dùng.console.logchỉ in ra màn hình cho developer xem.AI trả lời (typical):
returnsends a value back from a function.console.logoutputs a value to the console.- Both are ways to output values.
So sánh: AI đúng về cơ bản, nhưng dùng chung từ "output" cho cả hai gây hiểu nhầm. "Output" trong programming có thể hiểu là bất kỳ xuất dữ liệu nào, nhưng engineering-wise chúng khác layer hoàn toàn.
Điểm AI nói sai hoặc quá mơ hồ: "Both are ways to output values" — câu này khiến junior nghĩ chúng tương đương. Trong production, thay
returnbằngconsole.loglà bug nghiêm trọng.Kết luận: Nếu bạn chỉ ra được sự khác biệt về layer (caller vs console), bạn đã hiểu sâu hơn câu trả lời surface-level của AI. Concept này sẽ quay lại khi học về side effects và pure functions ở 0.5.7 và Stage 8 (React rendering).
14. Teach Back (Dạy lại)
Giả sử một junior developer hỏi bạn:
"Tại sao function của em
console.logra đúng giá trị, nhưng khi em gánconst result = myFunction(),resultlại làundefined?"
Hãy giải thích trong 2 phút, dùng đúng terminology: parameter, argument, return, side effect, caller.
Mô phỏng
- Bạn nói: Function của bạn đang dùng
console.log— đó là side effect, nghĩa là nó in ra console cho bạn nhìn thấy, nhưng không đưa giá trị về cho caller. Caller là chỗ bạn viếtconst result = myFunction(). Đểresultnhận được giá trị, function phải córeturn.returnlà cầu nối duy nhất đưa dữ liệu từ bên trong function ra ngoài.console.logchỉ là "nhìn cho vui",returnmới là "lấy để dùng".
💡 Tưởng tượng function như một máy ép nước cam:
returnlà vòi để rót nước ra ly.console.logchỉ là bạn nhìn vào máy và nói "ừ, có nước cam đấy" — nhưng ly của bạn vẫn trống.
Gợi ý đánh giá bản thân
- Đồng nghiệp có hiểu tại sao
resultlàundefinedkhông? - Bạn có dùng được ví dụ mới (không phải máy ép cam) để giải thích không?
- Nếu đồng nghiệp hỏi tiếp: "Vậy khi nào dùng
console.logtrong function?" — bạn trả lời được không?
15. Assessment (Đánh giá)
| Objective (Mục tiêu) | Hình thức đánh giá | Task |
|---|---|---|
| Distinguish parameter vs argument (Phân biệt parameter và argument) | Prediction (Dự đoán) | Câu 4, Prediction Exercise |
| Predict missing return (Dự đoán khi thiếu return) | Prediction (Dự đoán) | Câu 1, Prediction Exercise |
| Debug console.log vs return (Gỡ lỗi nhầm lẫn console.log và return) | Debug Lab | Debug Lab Section |
| Write valid function declaration (Viết function declaration đúng) | Implementation (Thực hành) | Implementation Lab Level 3 |
| Explain side effect vs return (Giải thích side effect và return) | Teach Back (Dạy lại) | Teach Back Section |
16. Exit Criteria (Tiêu chí qua bài)
- [ ] Có thể viết một function declaration có tên, parameters, body, và
return. - [ ] Có thể dự đoán đúng kết quả khi function thiếu
return(trả vềundefined). - [ ] Có thể dự đoán đúng kết quả khi argument thiếu (parameter nhận
undefined). - [ ] Có thể phân biệt
console.log(side effect) vàreturn(xuất giá trị cho caller). - [ ] Có thể trace luồng dữ liệu: argument → parameter → body → return → caller.
- [ ] Có thể giải thích cho người khác tại sao
const result = fn()raundefinedkhifnchỉ log.
17. Spiral Connection (Liên kết xoắn ốc)
Previous (Trước): Variables & Bindings (0.2) — bạn đã biết giá trị có thể gán vào biến. Giờ bạn học cách đóng góp nhiều dòng logic thành một "giá trị có thể gọi".
Current (Hiện tại): Function Declaration — function là đơn vị logic có tên, nhận input qua parameters, xuất output qua
return.
Next (Tiếp theo):
- 0.5.2 — Function Expression: function không chỉ được khai báo, mà còn là giá trị có thể gán vào biến.
- 0.5.3 — Arrow Functions: cú pháp ngắn gọn và sự khác biệt về
this.- Stage 1 — Execution Model: function declaration tạo ra binding trong scope như thế nào, hoisting, và closure.
- Stage 8 — React: component chính là function; props chính là arguments; return chính là JSX element.