Skip to content

Lesson 0.5.3 — Arrow Functions ​

Bài 0.5.3 — Arrow Functions
Syntax ngắn gọn, Return implicit & Parameter tricks • 24 phút
0:00 / 0:00

0. Metadata ​

FieldValue
Stage0 — JavaScript Language Foundation
Module0.5 — Functions
Lesson0.5.3
CompetencyC01.5 — Functions
Depth TargetL2–L3
PrerequisitesFunction Expression (0.5.2), Variables & Bindings (0.2.2)
Estimated Cognitive LoadMedium

1. Why This Exists (Vì sao cần học) ​

Bạn đã viết được function expression:

js
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:

js
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 return và console.log khá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/filter trong bài chỉ là context preview, không yêu cầu hiểu sâu array iteration.
  • Biết const / let và 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ể:

  1. Viết arrow function ở 3 dạng: block body, concise body, single parameter.
  2. Phân biệt arrow function và regular function expression ở mức syntax và behavior cơ bản.
  3. Dự đoán kết quả khi dùng concise body với object literal.
  4. Chọn giữa arrow function và regular function cho từng context đơn giản.
  5. 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:

text
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)
  • this behavior (để 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 Parameterx => x * 2 — bỏ ngoặc khi chỉ có một param.
No arguments objectArrow 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) ​

ConceptLý do chưa đào sâu
this lexically boundArrow 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 constructorsKhông dùng new với arrow function. Sẽ rõ hơn ở Stage 2 (prototype).
Not for object methodsDù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) ​

  • this mechanism (Stage 2)
  • Prototype / constructor (Stage 2)
  • Generator function (Supporting — Stage 3)
  • arguments object deep dive (Stage 1)

6. Worked Example (Ví dụ phân tích từng bước) ​

Xét đoạn code sau:

js
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 ​

js
const greet = name => "Hello " + name;
console.log(greet("An"));

Câu 2 ​

js
const makeUser = () => { name: "An", age: 20 };
console.log(makeUser());

Câu 3 ​

js
const items = [1, 2, 3];
const result = items.map((item, index) => {
  item * index;
});
console.log(result);

Câu 4 ​

js
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. age bị 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ề undefined mỗ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:

js
// 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]
js
// 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.
  • Function không có parameter vẫn cần ().

Level 2 — Partial Scaffold (Khung sẵn) ​

Hoàn thành để getUser trả về object đúng:

js
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]
js
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:

js
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.

js
// 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]
js
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ụ:

js
[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

js
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ý:

js
const fn = () => ({ a: 1 });

Block body quên return

js
const calc = (x, y) => {
  x + y;
};
console.log(calc(2, 3)); // undefined

Tạ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)

js
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:

js
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

js
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):

  • map chạ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:

js
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:

js
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 + return rõ 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:

js
const utils = {
  add: (a, b) => a + b,
  log: msg => console.log(msg),
  getName: () => this.name
};
js
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:

  1. Option A có vấn đề gì với getName? (Gợi ý: arrow + this)
  2. log trong Option A có ổn không? Tại sao?
  3. Bạn chọn option nào? Giải thích trade-off.
[Đáp án tham khảo]
  • Bạn nghĩ:

    1. getName dùng arrow nên this không trỏ đến utils. this.name sẽ là undefined (hoặc global name). Đây là lỗi silent.
    2. log trong Option A ổn vì nó không dùng this, chỉ dùng parameter msg. Arrow function hoạt động tốt ở đây.
    3. Chọn Option B hoặc kết hợp: arrow cho pure utility không cần this, regular function cho method cần this.
  • 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 this riê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):

js
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:

  1. Tại sao this.text là undefined?
  2. Sửa lại để render truy cập được Button.text.
[Đáp án tham khảo]
  • Bạn nghĩ:

    1. render là arrow function. Arrow function không bind this riêng; nó lấy this từ scope bên ngoài (global/undefined). Do đó this.text không phải Button.text.

    2. Sửa thành regular function:

      js
      render: function() {
        return "<button>" + this.text + "</button>";
      }

      Hoặc dùng method shorthand:

      js
      render() {
        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ểu this behavior.

13. AI-assisted Exercise (Bài tập với AI) ​

Level B — Challenge

  1. 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.
  2. Hỏi AI: "When should I use arrow functions vs regular functions in JavaScript?"
  3. 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?
  4. 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ần this, 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 this which is useful in some cases.
    • Regular functions are better when you need this or arguments.
  • 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 * 2 như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ết return, không thì ra undefined. 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 } ra undefined khô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 LabDebug Lab Section
Choose arrow vs regular (Chọn arrow hay regular)Design DecisionDesign Exercise
Explain concise vs block body (Giải thích concise và block)Teach BackTeach Back Section
Recognize arrow misuse in object method (Nhận biết lỗi dùng arrow làm object method)Production ScenarioProduction 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ả undefined khi block body thiếu return.
  • [ ] 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 bind this riêng — điểm quan trọng nhất nhưng cần hiểu this trước.
  • Stage 8 — React: functional component thường dùng arrow function; event handler dùng arrow để tránh this binding issue.
📴 Offline Mode — Content served from cache