Lesson 0.5.3 — Arrow Functions
0. Metadata
| Field | Value |
|---|---|
| Stage | 0 — JavaScript Language Foundation |
| Module | 0.5 — Functions |
| Lesson | 0.5.3 |
| Competency | C01.5 — Functions |
| Depth Target | L2–L3 |
| Prerequisites | Function Expression (0.5.2), Variables & Bindings (0.2.2) |
| Estimated Cognitive Load | Medium |
1. Why This Exists (Vì sao cần học)
Bạn đã viết được function expression:
const add = function(a, b) {
return a + b;
};Nhưng trong thực tế — đặc biệt khi truyền callback — bạn sẽ thấy code như thế này xuất hiện liên tục:
const add = (a, b) => a + b;
const doubled = items.map(item => item * 2);Vấn đề cốt lõi
Function expression dài dòng khi logic đơn giản. Arrow function cung cấp cú pháp ngắn gọn để tạo function value nhanh hơn, đặc biệt cho callback một dòng. Nhưng cú pháp ngắn ẩn chứa rủi ro: implicit return, this behavior khác biệt, và không phù hợp cho mọi context.
Ở Stage 0, bạn học arrow function như một công cụ syntax. Cơ chế this sâu hơn thuộc Stage 2.
Nếu không hiểu arrow function, bạn sẽ không đọc được phần lớn code JavaScript hiện đại — từ React components đến array methods.
2. Prerequisites (Yêu cầu đầu vào)
Trước khi học bài này, bạn cần:
- Viết được function expression và gán vào biến (0.5.2).
- Hiểu
returnvàconsole.logkhác nhau (0.5.1). - Hiểu callback pattern ở mức cơ bản: một hàm có thể truyền vào hàm khác như argument (0.5.2). Các ví dụ dùng
map/filtertrong bài chỉ là context preview, không yêu cầu hiểu sâu array iteration. - Biết
const/letvà block scope cơ bản (0.2.2).
WARNING
Nếu bạn chưa chắc const add = function(a, b) { return a + b; } hoạt động thế nào, quay lại 0.5.2 trước. Arrow function chỉ là syntax khác trên cùng một mental model.
3. Learning Objectives (Mục tiêu học tập)
Sau bài này, bạn có thể:
- Viết arrow function ở 3 dạng: block body, concise body, single parameter.
- Phân biệt arrow function và regular function expression ở mức syntax và behavior cơ bản.
- Dự đoán kết quả khi dùng concise body với object literal.
- Chọn giữa arrow function và regular function cho từng context đơn giản.
- Nhận biết lỗi phổ biến khi arrow function dùng làm object method.
4. Mental Model (Mô hình tư dụ)
Hãy hình dung arrow function như một phiên bản rút gọn của function expression:
Regular Expression Arrow Function
─────────────────────────────────────────────────
function(x) { (x) => {
return x * 2; return x * 2;
} }
function(x) { (x) => x * 2
return x * 2;
}
function(x, y) { (x, y) => x + y
return x + y;
}Mental Model
Arrow function = Expression Syntax Shortcut
↓
Same rules: parameters → body → return value
↓
Difference 1: Concise body auto-return (không cần chữ "return")
Difference 2: Single param bỏ ngoặc được
Difference 3: Không có "function" keyword
Difference 4: Không bind "this" riêng (awareness — Stage 2)Arrow function không tạo ra một "loại function" mới về mặt engine. Nó vẫn tạo ra một function value. Điểm khác biệt nằm ở:
- Cách viết (syntax)
thisbehavior (để nguyên từ outer scope)
5. Core Concepts (Các khái niệm cốt lõi)
Essential (Bắt buộc)
| Concept | Ý nghĩa |
|---|---|
| Arrow Syntax | (params) => body — tạo function value không dùng từ khóa function. |
| Concise Body | (x) => x * 2 — một biểu thức, tự động return. |
| Block Body | (x) => { return x * 2; } — nhiều dòng, cần return thủ công. |
| Single Parameter | x => x * 2 — bỏ ngoặc khi chỉ có một param. |
No arguments object | Arrow function không có arguments đặc biệt. |
Supporting (Hỗ trợ)
| Concept | Ý nghĩa |
|---|---|
| Parentheses required | (x, y) => ... hoặc () => ... — không bỏ ngoặc khi 0 hoặc nhiều params. |
| Returning object | () => ({ a: 1 }) — cần ngoặc đơn bao object để JS không hiểu nhầm là block. |
Awareness (Biết tồn tại)
| Concept | Lý do chưa đào sâu |
|---|---|
this lexically bound | Arrow function không tạo this riêng. Đây là điểm quan trọng nhất nhưng cần hiểu this trước (Stage 2). |
| Not for constructors | Không dùng new với arrow function. Sẽ rõ hơn ở Stage 2 (prototype). |
| Not for object methods | Dùng arrow làm method thường gây lỗi this. Sẽ học sâu ở Stage 2. |
Out of Scope (Không thuộc bài này)
thismechanism (Stage 2)- Prototype / constructor (Stage 2)
- Generator function (Supporting — Stage 3)
argumentsobject deep dive (Stage 1)
6. Worked Example (Ví dụ phân tích từng bước)
Xét đoạn code sau:
const prices = [100, 200, 300];
const withTax = prices.map(price => price * 1.1);
console.log(withTax);
const formatPrice = price => {
const formatted = price.toLocaleString();
return formatted + " VND";
};
console.log(formatPrice(1500000));Walkthrough
Step 1 — Concise Arrow in Callbackprice => price * 1.1 là arrow function với:
- Một parameter:
price(bỏ ngoặc) - Concise body:
price * 1.1(tự động return)
map gọi arrow function này 3 lần, mỗi lần với một giá trị từ prices.
Step 2 — ResultwithTax nhận [110, 220, 330].
Step 3 — Block Body Arrowprice => { ... } là arrow function với block body. Vì có dấu {}, JavaScript hiểu đây là block statement, không phải concise expression. Do đó cần return thủ công.
Step 4 — ExecutionformatPrice(1500000):
formatted="1,500,000"return "1,500,000 VND"
Key Insight: Dấu {} thay đổi hoàn toàn ý nghĩa của body. Có {} = block (cần return). Không {} = expression (auto-return).
7. Prediction Exercise (Bài tập dự đoán)
Đừng chạy code. Đọc và dự đoán output hoặc lỗi, sau đó giải thích tại sao.
Câu 1
const greet = name => "Hello " + name;
console.log(greet("An"));Câu 2
const makeUser = () => { name: "An", age: 20 };
console.log(makeUser());Câu 3
const items = [1, 2, 3];
const result = items.map((item, index) => {
item * index;
});
console.log(result);Câu 4
const multiply = (a, b) => a * b;
console.log(multiply(4, 5));[Đáp án & Giải thích]
Câu 1: "Hello An"
- Giải thích: Một param
name, bỏ ngoặc. Concise body tự động return"Hello " + name.
Câu 2: SyntaxError (hoặc unexpected token)
- Giải thích:
{ name: "An", age: 20 }bị JavaScript hiểu nhầm là block body, không phải object literal.name:bị hiểu là label trong statement.agebị hiểu là identifier expression, dấu:sau đó gây lỗi.
Câu 3: [undefined, undefined, undefined]
- Giải thích: Có
{}nên là block body. Bên trong block chỉ cóitem * index;— một expression statement, không córeturn. Function trả vềundefinedmỗi lần gọi.
Câu 4: 20
- Giải thích: Hai params cần ngoặc. Concise body tự động return
20.
8. Implementation Lab (Bài lab thực hành)
Level 1 — Guided (Có hướng dẫn)
Viết lại các function expression sau thành arrow function:
// A
const square = function(x) {
return x * x;
};
// B
const greet = function(name) {
return "Hi, " + name;
};
// C
const getRandom = function() {
return Math.random();
};[Đáp án tham khảo]
// A
const square = x => x * x;
// B
const greet = name => "Hi, " + name;
// C
const getRandom = () => Math.random();Giải thích:
- Khi chỉ có một parameter, có thể bỏ dấu
(). - Khi function chỉ có một biểu thức trả về, có thể bỏ
{}vàreturn. Functionkhông có parameter vẫn cần().
Level 2 — Partial Scaffold (Khung sẵn)
Hoàn thành để getUser trả về object đúng:
const getUser = (name, age) => /* sửa chỗ này */ {
name: name,
age: age
};Gợi ý
Object literal trong concise body cần được bao bởi dấu (). Tại sao? Xem lại Câu 2 ở Prediction Exercise.
[Đáp án tham khảo]
const getUser = (name, age) => ({
name: name,
age: age
});
console.log(getUser("An", 20));
// { name: "An", age: 20 }Giải thích:
Arrow function muốn return trực tiếp một object literal cần bọc object bằng ().
Nếu không có (), {} sẽ được hiểu là function body.
Có thể viết ngắn hơn bằng shorthand property:
const getUser = (name, age) => ({
name,
age
});Level 3 — Independent (Tự viết)
Viết một hàm processData nhận mảng số và một hàm transform (arrow function). Trả về mảng mới sau khi áp dụng transform.
// Ví dụ sử dụng:
const doubled = processData([1, 2, 3], x => x * 2);
console.log(doubled); // [2, 4, 6]
const negated = processData([1, 2, 3], x => -x);
console.log(negated); // [-1, -2, -3][Đáp án tham khảo]
const processData = (numbers, transform) => {
return numbers.map(transform);
};
const doubled = processData([1, 2, 3], x => x * 2);
console.log(doubled);
// [2, 4, 6]
const negated = processData([1, 2, 3], x => -x);
console.log(negated);
// [-1, -2, -3]Giải thích:
- numbers là mảng số cần xử lý.
- transform là arrow function dùng để biến đổi từng phần tử.
- map() tạo ra một mảng mới sau khi áp dụng transform lên từng phần tử.
Ví dụ:
[1, 2, 3].map(x => x * 2);
// [2, 4, 6]9. Edge Cases (Các trường hợp ngoại lệ)
Returning object literal
const fn = () => { a: 1 };Tại sao: { a: 1 } bị hiểu là block chứa label a:, không phải object.
Cách nhận biết: Không lỗi compile nhưng kết quả là undefined. Nếu có thêm code sau a: 1, có thể gây lỗi syntax khó hiểu.
Cách xử lý:
const fn = () => ({ a: 1 });Block body quên return
const calc = (x, y) => {
x + y;
};
console.log(calc(2, 3)); // undefinedTại sao: Có {} nên là block. x + y; là expression statement, không return.
Cách nhận biết: Function trả về undefined dù nhìn có vẻ "tính toán".
Cách xử lý: Thêm return hoặc bỏ {} nếu chỉ có một expression.
Không dùng arrow cho object method (cơ bản)
const user = {
name: "An",
greet: () => {
return "Hello " + this.name;
}
};
console.log(user.greet()); // "Hello undefined"Tại sao: Arrow function không tạo this riêng. this ở đây trỏ đến global object (hoặc undefined ở strict mode), không phải user.
Cách nhận biết: this.name là undefined dù object có property name.
Cách xử lý: Dùng regular function expression hoặc method shorthand:
const user = {
name: "An",
greet() {
return "Hello " + this.name;
}
};Nhắc nhở: Chi tiết this thuộc Stage 2. Ở đây bạn chỉ cần nhớ: đừng dùng arrow function làm object method nếu cần truy cập this của object.
10. Debug Lab (Bài lab gỡ lỗi)
Symptom (Triệu chứng): Mảng kết quả toàn undefined
const products = [
{ name: "A", price: 100 },
{ name: "B", price: 200 }
];
const labels = products.map(product => {
name: product.name,
price: product.price + " VND"
});
console.log(labels); // [undefined, undefined]Reproduction (Tái hiện lỗi): Chạy code trên.
Evidence (Bằng chứng):
mapchạy không lỗi.- Kết quả là
[undefined, undefined]thay vì mảng object.
Hypothesis (Giả thuyết): Arrow function có block body {} nhưng không có return. JavaScript hiểu name: là label, product.name là expression, dấu , là comma operator... hoặc nếu parse được thì vẫn không return.
Verification (Xác minh): Thử thêm return:
const labels = products.map(product => {
return {
name: product.name,
price: product.price + " VND"
};
});Kết quả đúng. Hoặc dùng concise body với ngoặc đơn:
const labels = products.map(product => ({
name: product.name,
price: product.price + " VND"
}));Root Cause (Nguyên nhân gốc rễ): Nhầm lẫn giữa block body và concise body. Developer muốn return object literal nhưng cú pháp {} bị hiểu là block statement.
Fix (Sửa): Dùng return trong block body, hoặc bao object bằng () trong concise body.
Prevention (Phòng ngừa):
- Khi arrow function trả về object, luôn hỏi: "Tôi có cần ngoặc đơn không?"
- Nếu body phức tạp hơn một dòng, dùng block body +
returnrõ ràng.
11. Design Exercise (Bài tập thiết kế giải pháp)
Bạn đang review code trong một file utility:
const utils = {
add: (a, b) => a + b,
log: msg => console.log(msg),
getName: () => this.name
};const utils = {
add: (a, b) => a + b,
log: function(msg) { console.log(msg); },
getName: function() { return this.name; }
};Context: utils.name được set từ bên ngoài.
Câu hỏi:
- Option A có vấn đề gì với
getName? (Gợi ý: arrow +this) logtrong Option A có ổn không? Tại sao?- Bạn chọn option nào? Giải thích trade-off.
[Đáp án tham khảo]
Bạn nghĩ:
getNamedùng arrow nênthiskhông trỏ đếnutils.this.namesẽ làundefined(hoặc global name). Đây là lỗi silent.logtrong Option A ổn vì nó không dùngthis, chỉ dùng parametermsg. Arrow function hoạt động tốt ở đây.- Chọn Option B hoặc kết hợp: arrow cho pure utility không cần
this, regular function cho method cầnthis.
Kết luận: Arrow function không phải "cách viết mới thay thế hoàn toàn". Nó là công cụ cho context không cần
thisriêng. Việc chọn sai context tạo ra bug khó phát hiện.
12. Production Scenario (Tình huống thực tế)
Context: Một React component (bạn sẽ học React ở Stage 8, nhưng đây là pattern phổ biến):
const Button = {
text: "Submit",
render: () => {
return "<button>" + this.text + "</button>";
}
};
console.log(Button.render());Symptom: Button render ra "<button>undefined</button>" thay vì "<button>Submit</button>".
Constraint: Không được đổi cấu trúc object. Chỉ sửa cách khai báo render.
Câu hỏi:
- Tại sao
this.textlàundefined? - Sửa lại để
rendertruy cập đượcButton.text.
[Đáp án tham khảo]
Bạn nghĩ:
renderlà arrow function. Arrow function không bindthisriêng; nó lấythistừ scope bên ngoài (global/undefined). Do đóthis.textkhông phảiButton.text.Sửa thành regular function:
jsrender: function() { return "<button>" + this.text + "</button>"; }Hoặc dùng method shorthand:
jsrender() { return "<button>" + this.text + "</button>"; }
Bài học: Arrow function trong object method là một trong những bug phổ biến nhất trong codebase JavaScript. Nó không crash — nó chỉ silently trả về
undefined, rất khó debug nếu không hiểuthisbehavior.
13. AI-assisted Exercise (Bài tập với AI)
Level B — Challenge
- Tự trả lời trước: Liệt kê 2 trường hợp NÊN dùng arrow function và 1 trường hợp KHÔNG NÊN.
- Hỏi AI: "When should I use arrow functions vs regular functions in JavaScript?"
- So sánh câu trả lời AI với câu của bạn. AI có nói "arrow functions are always shorter so use them everywhere" không?
- Verify bằng MDN: tìm trang "Arrow functions" trên MDN, đọc phần "No binding of
this".
Gợi ý
AI thường khuyến khích dùng arrow function cho mọi thứ vì "ngắn gọn" và "modern". Nếu AI không nhắc đến object method hoặc constructor là nơi KHÔNG nên dùng arrow, bạn đã tìm ra điểm mù. Đây là lý do tại sao documentation > AI opinion.
Đáp án tham khảo
Bạn nghĩ: Nên dùng: callback đơn giản (map/filter), function không cần
this. Không nên: object method cầnthis, constructor function.AI trả lời (typical):
- Arrow functions are shorter and cleaner.
- Use them for callbacks and short functions.
- They don't have their own
thiswhich is useful in some cases. - Regular functions are better when you need
thisorarguments.
So sánh: AI đúng về callback nhưng thường mơ hồ về "some cases". AI hiếm khi nhấn mạnh rằng arrow trong object method là bug chờ xảy ra, không chỉ là "preference".
Điểm AI nói sai hoặc quá mơ hồ: "Use them for callbacks and short functions" — câu này quá rộng. Một callback trong object method vẫn có thể cần
this. AI thường thiếu context cụ thể về object method pattern.Kết luận: Nếu bạn chỉ ra được rằng "ngắn gọn" không đồng nghĩa "đúng mọi nơi", bạn đã hiểu sâu hơn câu trả lời generic của AI. Concept này sẽ quay lại ở Stage 2 (
this) và Stage 8 (React class vs function component).
14. Teach Back (Dạy lại)
Giả sử một junior developer hỏi bạn:
"Sao em thấy người ta viết
x => x * 2nhưng cũng có lúc viết(x) => { return x * 2; }. Khi nào bỏ ngoặc, khi nào cần return?"
Hãy giải thích trong 2 phút, dùng ít nhất một ví dụ về trả về object literal.
Mô phỏng
- Bạn nói: Có hai dạng arrow function. Dạng ngắn gọi là concise body:
x => x * 2. Ở đây không có dấu ngoặc nhọn, JavaScript tự động return kết quả của biểu thức. Dạng thứ hai là block body:(x) => { return x * 2; }. Có ngoặc nhọn thì bạn phải tự viếtreturn, không thì raundefined. Còn trả về object? Bạn phải viết() => ({ a: 1 })— bao object bằng ngoặc đơn. Nếu không, JavaScript tưởng{ a: 1 }là block code, không phải object.
💡 Tưởng tượng dấu
=>như mũi tên chỉ đến kết quả. Nếu kết quả nằm sau mũi tên trực tiếp (không có rào cản{}), JavaScript tự động đẩy kết quả về. Nhưng nếu bạn dựng một bức tường{}, JavaScript nghĩ bạn muốn nhiều phòng bên trong, và bạn phải tự mang kết quả ra (return). Object literal cũng dùng{}nên dễ bị nhầm với tường — cần thêm ngoặc đơn()để nói "đây là giá trị, không phải tường".
Gợi ý đánh giá bản thân
- Đồng nghiệp có hiểu tại sao
() => { a: 1 }raundefinedkhông? - Bạn có giải thích được mà không dùng từ "hoisting" hay "scope" không? (Chưa học ở Stage 0)
- Nếu đồng nghiệp hỏi: "Vậy khi nào dùng block body?" — bạn trả lời được không? (Gợi ý: khi cần nhiều dòng, tạo biến tạm, hoặc debug breakpoint.)
15. Assessment (Đánh giá)
| Objective (Mục tiêu) | Hình thức đánh giá | Task |
|---|---|---|
| Write arrow function 3 forms (Viết arrow function 3 dạng) | Implementation (Thực hành) | Implementation Lab Level 1 |
| Predict object literal return (Dự đoán return object literal) | Prediction (Dự đoán) | Câu 2, Prediction Exercise |
| Debug missing return in block body (Gỡ lỗi thiếu return) | Debug Lab | Debug Lab Section |
| Choose arrow vs regular (Chọn arrow hay regular) | Design Decision | Design Exercise |
| Explain concise vs block body (Giải thích concise và block) | Teach Back | Teach Back Section |
| Recognize arrow misuse in object method (Nhận biết lỗi dùng arrow làm object method) | Production Scenario | Production Scenario Section |
16. Exit Criteria (Tiêu chí qua bài)
- [ ] Có thể viết arrow function ở cả 3 dạng: single param bỏ ngoặc, nhiều param có ngoặc, block body và concise body.
- [ ] Có thể dự đoán kết quả khi concise body trả về object literal (cần ngoặc đơn).
- [ ] Có thể dự đoán kết quả
undefinedkhi block body thiếureturn. - [ ] Có thể phân biệt khi nào arrow function phù hợp và khi nào regular function phù hợp (ở mức cơ bản: object method cần tránh arrow).
- [ ] Có thể refactor function expression thành arrow function đúng cú pháp.
- [ ] Có thể giải thích cho người khác tại sao
() => { a: 1 }không trả về object.
17. Spiral Connection (Liên kết xoắn ốc)
Previous (Trước): Function Expression (0.5.2) — bạn đã biết function là giá trị có thể gán vào biến. Arrow function là cú pháp rút gọn để tạo cùng một loại giá trị đó.
Current (Hiện tại): Arrow Functions — cú pháp ngắn gọn, concise body auto-return, và giới hạn cơ bản về
this/ object method.
Next (Tiếp theo):
- 0.5.4 — Parameters: default params, rest params, destructuring params. Arrow function vẫn áp dụng đầy đủ.
- 0.5.5 — Callback: arrow function là công cụ chính để viết callback ngắn gọn trong
map,filter,setTimeout.- Stage 2 —
this& Object Model: arrow function không bindthisriêng — điểm quan trọng nhất nhưng cần hiểuthistrước.- Stage 8 — React: functional component thường dùng arrow function; event handler dùng arrow để tránh
thisbinding issue.