Skip to content

Lesson 0.5.6 — Higher-order Functions ​

Bài 0.5.6 — Higher-order Functions
Function nhận function & Function trả về function • 25 phút
0:00 / 0:00

0. Metadata ​

FieldValue
Stage0 — JavaScript Language Foundation
Module0.5 — Functions
Lesson0.5.6
CompetencyC01.5 — Functions
Depth TargetL2–L3
PrerequisitesCallback (0.5.5), Function Expression (0.5.2), Arrow Functions (0.5.3)
Estimated Cognitive LoadMedium–High

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

Bạn đã biết truyền function vào function khác (callback). Nhưng trong thực tế, một function còn có thể tạo ra và trả về một function khác:

js
function makeMultiplier(factor) {
  return function(number) {
    return number * factor;
  };
}

const double = makeMultiplier(2);
console.log(double(5)); // 10

Vấn đề cốt lõi

Khi bạn viết const double = makeMultiplier(2), bạn không nhận lại một con số. Bạn nhận lại một công cụ mới — một function đã được "cấu hình sẵn" với factor = 2. Đây là higher-order function (HOF): function tác động lên function (nhận vào hoặc trả về).

HOF giúp bạn:

  • Tạo ra các function chuyên biệt từ một khuôn mẫu (factory).
  • Tách logic "cấu hình" khỏi logic "thực thi".
  • Giảm lặp code khi nhiều nơi cần behavior tương tự nhưng khác tham số.

Ở Stage 0, bạn học HOF như một pattern sử dụng function. Cơ chế "tại sao returned function vẫn nhớ factor" (closure) thuộc Stage 1.

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à arrow function (0.5.2–0.5.3).
  • Hiểu callback pattern: truyền function vào parameter (0.5.5).
  • Hiểu return có thể trả về bất kỳ giá trị nào, bao gồm function (0.5.1).
  • Biết typeof với function value trả về "function" (0.2.5).

WARNING

Nếu bạn chưa chắc return x trong function body sẽ đưa giá trị x ra ngoài cho caller, quay lại 0.5.1. HOF chỉ là việc x ở đây chính là một function.

3. Learning Objectives (Mục tiêu học tập) ​

Sau bài này, bạn có thể:

  1. Nhận diện một higher-order function (nhận function làm param hoặc trả về function).
  2. Viết một function factory trả về function đã được cấu hình sẵn.
  3. Gọi function được trả về từ HOF: factory()() hoặc gán vào biến rồi gọi.
  4. Phân biệt giá trị trả về là function (cần gọi thêm) và giá trị trả về là dữ liệu.
  5. Dự đoán output khi HOF kết hợp với callback.

4. Mental Model (Mô hình tư duy) ​

Hãy hình dung function thông thường như một máy xay sinh tố:

text
Máy xay
  ↓
Nhận: trái cây
  ↓
Trả về: nước ép

Higher-order function như một nhà máy sản xuất máy xay:

text
Nhà máy (HOF)
  ↓
Nhận: công suất motor (config)
  ↓
Trả về: một chiếc máy xay mới (function)
  ↓
Chiếc máy xay đó:
  - Đã được cài đặt công suất
  - Sẵn sàng nhận trái cây và ép

Mental Model

Regular Function
  Input: data → Output: data

Higher-Order Function (HOF)
  ├─ Input: function → Output: data     (callback side: map, filter)
  ├─ Input: data → Output: function     (factory side: makeMultiplier)
  └─ Input: function → Output: function (compose: awareness)

Quan trọng: Khi HOF trả về một function, caller nhận được một công cụ chưa chạy. Caller phải gọi thêm một lần nữa để lấy kết quả cuối cùng.

Ở Stage 0, bạn quan sát được rằng returned function "vẫn biết" giá trị từ lúc tạo (ví dụ: factor = 2). Đừng lo lắng về cơ chế bên trong — đó là closure, sẽ học ở Stage 1.

5. Core Concepts (Các khái niệm cốt lõi) ​

Essential (Bắt buộc) ​

ConceptÝ nghĩa
Higher-Order Function (HOF)Function nhận function làm argument, hoặc trả về một function, hoặc cả hai.
Function FactoryHOF trả về function: dùng data để "cấu hình" và tạo ra function chuyên biệt.
Specialized FunctionFunction được tạo ra từ factory, đã "khóa" một số giá trị config.
Double Invocationfactory(config)(data) — gọi factory để nhận function, rồi gọi function đó ngay.

Supporting (Hỗ trợ) ​

ConceptÝ nghĩa
Partial ApplicationTạo function mới bằng cách cố định trước một số argument.
Function CompositionKết hợp nhiều function thành một: compose(f, g)(x) = f(g(x)).

Awareness (Biết tồn tại) ​

ConceptLý do chưa đào sâu
ClosureCơ chế cho phép returned function truy cập biến từ scope ngoài. Sẽ học sâu ở Stage 1.
CurryingBiến function nhiều param thành chuỗi function một param. Thuộc functional programming nâng cao.
Compose/Pipe deep diveKết hợp nhiều HOF. Sẽ gặp lại ở Stage 8 (React hooks) và Stage 12 (architecture).

Out of Scope (Không thuộc bài này) ​

  • Closure mechanism / lexical environment (Stage 1)
  • this binding trong returned function (Stage 2)
  • Advanced functional programming (monad, functor — Elective)
  • Decorator pattern (Stage 12)
  • Memoization / throttle / debounce (Stage 8/11)

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

Xét đoạn code sau:

js
function makeTaxCalculator(rate) {
  return function(price) {
    return Math.round(price * (1 + rate));
  };
}

const vat10 = makeTaxCalculator(0.1);
const vat8 = makeTaxCalculator(0.08);

console.log(vat10(100));
console.log(vat8(100));
console.log(typeof vat10);

Walkthrough

Step 1 — Factory CallmakeTaxCalculator(0.1) được gọi. rate nhận giá trị 0.1. Body tạo ra một function mới và return nó ra ngoài.

Step 2 — Assignmentvat10 nhận function value được trả về. Lúc này vat10 là một function, chưa được gọi.

Step 3 — Specialization Function trong vat10 "nhớ" rằng rate là 0.1. (Ở Stage 0, bạn chỉ cần quan sát behavior này. Cơ chế gọi là closure, học sau.)

Step 4 — Second Callvat10(100) gọi function được tạo ra. price = 100. Tính 100 * 1.1 = 110. Math.round trả về 110.

Step 5 — Another SpecializationmakeTaxCalculator(0.08) tạo ra một function khác, với rate = 0.08. vat8 giữ function này.

Step 6 — Resultvat8(100) → 108.

Step 7 — Type Checktypeof vat10 là "function" — xác nhận vat10 là một function value, không phải number.

Key Insight: Một khuôn mẫu (makeTaxCalculator) tạo ra nhiều công cụ khác nhau (vat10, vat8). Không cần viết lại logic tính toán cho mỗi mức thuế.

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
function makeGreeter(greeting) {
  return function(name) {
    return greeting + " " + name;
  };
}
const sayHi = makeGreeter("Hi");
console.log(sayHi("An"));

Câu 2 ​

js
function makeGreeter(greeting) {
  return function(name) {
    return greeting + " " + name;
  };
}
console.log(makeGreeter("Hello"));

Câu 3 ​

js
function makeAdder(x) {
  return y => x + y;
}
console.log(makeAdder(5)(3));
console.log(typeof makeAdder(5));

Câu 4 ​

js
function applyTwice(fn, value) {
  return fn(fn(value));
}
const result = applyTwice(x => x * 2, 3);
console.log(result);
[Đáp án & Giải thích]

Câu 1: "Hi An"

  • Giải thích: makeGreeter("Hi") trả về một function. sayHi giữ function đó. Gọi sayHi("An") chạy returned function với name = "An" và greeting = "Hi".

Câu 2: [Function (anonymous)] (hoặc tương tự)

  • Giải thích: makeGreeter("Hello") trả về một function. console.log in ra representation của function, không phải chuỗi. Chưa có lần gọi thứ hai với name.

Câu 3: 8, "function"

  • Giải thích: makeAdder(5)(3) = gọi makeAdder(5) nhận về function, rồi gọi ngay với (3). x = 5, y = 3, 5 + 3 = 8. typeof makeAdder(5) là "function" vì makeAdder trả về function.

Câu 4: 12

  • Giải thích: applyTwice nhận fn = x => x * 2 và value = 3. fn(value) = 6. fn(6) = 12. Return 12.

8. Implementation Lab (Bài lab thực hành) ​

Level 1 — Guided (Có hướng dẫn) ​

Viết function factory makePrefixer nhận một chuỗi prefix và trả về một function. Function được trả về nhận một chuỗi text và trả về prefix + text.

js
const error = makePrefixer("[ERROR] ");
console.log(error("Disk full")); // "[ERROR] Disk full"
[Đáp án tham khảo]
js
function makePrefixer(prefix) {
  return function(text) {
    return prefix + text;
  };
}

const error = makePrefixer("[ERROR] ");

console.log(error("Disk full"));
// "[ERROR] Disk full"

Giải thích:

  • makePrefixer là một function factory vì nó tạo và trả về một function mới.
  • Function bên trong closure lại giá trị prefix từ function bên ngoài.
  • Khi gọi error("Disk full"), giá trị prefix vẫn được giữ lại là "[ERROR] ".

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

Hoàn thành createValidator — một factory tạo ra hàm kiểm tra độ dài tối thiểu:

js
function createValidator(minLength) {
  // return một function nhận value và return boolean
}

const isLongEnough = createValidator(5);
console.log(isLongEnough("hello")); // true
console.log(isLongEnough("hi"));    // false
[Đáp án tham khảo]
js
function createValidator(minLength) {
  return function(value) {
    return value.length >= minLength;
  };
}

const isLongEnough = createValidator(5);

console.log(isLongEnough("hello"));
// true

console.log(isLongEnough("hi"));
// false

Giải thích:

  • createValidator nhận minLength và trả về một function.
  • Function được trả về nhận value và kiểm tra value.length >= minLength.
  • Function bên trong vẫn truy cập được minLength nhờ closure.
  • Mỗi lần gọi createValidator() có thể tạo ra một validator với điều kiện khác nhau.

Ví dụ:

js
const isAtLeast10 = createValidator(10);

console.log(isAtLeast10("JavaScript"));
// true

Level 3 — Independent (Tự viết) ​

Viết function compose nhận vào hai function f và g. Trả về một function mới nhận x và trả về f(g(x)).

js
// Ví dụ:
const add1 = x => x + 1;
const double = x => x * 2;

const add1ThenDouble = compose(double, add1);
console.log(add1ThenDouble(5)); // 12 (vì: 5 + 1 = 6, rồi 6 * 2 = 12)

Gợi ý

compose trả về function(x) { return f(g(x)); }. Đây là nền tảng của functional pipeline.

[Đáp án tham khảo]
js
function compose(f, g) {
  return function(x) {
    return f(g(x));
  };
}

const add1 = x => x + 1;
const double = x => x * 2;

const add1ThenDouble = compose(double, add1);

console.log(add1ThenDouble(5));
// 12

Giải thích:

compose(f, g) tạo ra một function mới và thực hiện g trước, sau đó mới thực hiện f.

Với:

js
const add1ThenDouble = compose(double, add1);

quá trình xử lý là:

text
5
→ add1(5)
→ 6
→ double(6)
→ 12
  • g(x) được thực hiện trước.
  • Kết quả của g(x) được truyền vào f().
  • Cuối cùng f(g(x)) là kết quả được return.

9. Edge Cases (Các trường hợp ngoại lệ) ​

Quên gọi function được trả về

js
const discount = makeDiscount(20);
if (discount) {
  console.log("Valid!"); // Luôn chạy
}

Tại sao: discount là một function. Function luôn là truthy. Kiểm tra if (discount) không kiểm tra được logic validation.

Cách nhận biết: Điều kiện luôn đúng dù dữ liệu không hợp lệ.

Cách xử lý: Gọi function để nhận boolean: if (discount(price)). Hoặc đặt tên rõ ràng: const makeDiscount = ... để nhớ đây là factory.

Nhầm return fn với return fn()

js
function factory() {
  const fn = () => 42;
  return fn(); // Trả về 42, không phải function
}
const result = factory();
console.log(typeof result); // "number"

Tại sao: fn() gọi function ngay lập tức và trả về kết quả (42). Nếu muốn trả về chính function, phải viết return fn.

Cách nhận biết: typeof kết quả không phải "function" khi bạn mong đợi một function.

Cách xử lý: Kiểm tra intent: bạn muốn trả về "công thức" hay "kết quả của công thức"?

Double invocation khó đọc

js
const result = makeTaxer(0.1)(100);

Tại sao: Hợp lệ về syntax nhưng khó đọc cho người mới. Không rõ makeTaxer(0.1) trả về gì.

Cách nhận biết: Code review flag — "what does this return?"

Cách xử lý: Tách thành hai dòng:

js
const taxer = makeTaxer(0.1);
const result = taxer(100);

Factory tạo function mới mỗi lần gọi

js
const a = makeMultiplier(2);
const b = makeMultiplier(2);
console.log(a === b); // false

Tại sao: Mỗi lần gọi factory tạo ra một function value mới, ở vùng nhớ khác nhau.

Cách nhận biết: So sánh identity (===) luôn false dù logic giống nhau.

Cách xử lý: Đây là behavior đúng. Nếu cần reuse, gán vào biến một lần.

10. Debug Lab (Bài lab gỡ lỗi) ​

Symptom (Triệu chứng): Validation luôn pass dù input quá ngắn.

js
function makeLengthValidator(min) {
  return function(value) {
    return value.length >= min;
  };
}

const isValid = makeLengthValidator(5);

// Junior kiểm tra:
if (isValid) {
  console.log("Input is valid!"); // Luôn in ra, kể cả với "hi"
}

Reproduction (Tái hiện lỗi): Chạy code với input "hi". Quan sát "Input is valid!" vẫn xuất hiện.

Evidence (Bằng chứng):

  • isValid không phải true/false.
  • console.log(isValid) in ra [Function].

Hypothesis (Giả thuyết): Developer nhầm lẫn giữa function được trả về (isValid là callable) và kết quả boolean (isValid("hi")). if (isValid) kiểm tra truthiness của function object — function luôn truthy.

Verification (Xác minh): Thay if (isValid) bằng if (isValid("hi")). Kết quả đúng: "hi".length >= 5 là false, không in ra.

Root Cause (Nguyên nhân gốc rễ): Factory function (makeLengthValidator) trả về một function. Biến isValid chứa function đó, không phải kết quả của nó. Để có kết quả, phải gọi isValid(value).

Fix (Sửa):

js
const input = "hi";
if (isValid(input)) {
  console.log("Input is valid!");
} else {
  console.log("Input too short!");
}

Prevention (Phòng ngừa):

  • Đặt tên factory bắt đầu bằng make hoặc create để nhắc nhở nó trả về một function.
  • Khi nhận giá trị từ HOF, hỏi: "Đây là dữ liệu hay công cụ cần gọi thêm?"
  • Dùng typeof để kiểm tra nếu nghi ngờ.

11. Design Exercise (Bài tập thiết kế giải pháp) ​

Bạn cần viết một hệ thống format log cho nhiều module:

js
function logAuth(message) {
  return `[AUTH] ${new Date().toISOString()}: ${message}`;
}
function logPayment(message) {
  return `[PAYMENT] ${new Date().toISOString()}: ${message}`;
}
function logInventory(message) {
  return `[INVENTORY] ${new Date().toISOString()}: ${message}`;
}
js
function makeLogger(module) {
  return function(message) {
    return `[${module}] ${new Date().toISOString()}: ${message}`;
  };
}

const logAuth = makeLogger("AUTH");
const logPayment = makeLogger("PAYMENT");
const logInventory = makeLogger("INVENTORY");

Câu hỏi:

  1. Option A có vấn đề gì nếu bạn cần thay đổi format timestamp?
  2. Option B có nhược điểm gì so với Option A?
  3. Nếu bạn cần thêm 10 module nữa, option nào scale tốt hơn?
[Đáp án tham khảo]
  • Bạn nghĩ:

    1. Option A phải sửa timestamp ở mỗi function. Dễ quên một chỗ, dẫn đến format không nhất quán.
    2. Option B khó đọc hơn với developer mới. Có thêm một lớp abstraction (factory).
    3. Option B. Thêm module = một dòng makeLogger("NEW_MODULE"). Không đụng vào logic format.
  • Kết luận: HOF factory tách biệt "cái gì cần log" (module name) khỏi "log như thế nào" (format). Đây là separation of concerns cơ bản.

12. Production Scenario (Tình huống thực tế) ​

Context: Một e-commerce platform có nhiều loại khuyến mãi. Junior viết code như sau:

js
function calculatePrice(price, type) {
  if (type === "gold") return price * 0.8;
  if (type === "silver") return price * 0.9;
  if (type === "member") return price * 0.95;
  return price;
}

// Sử dụng ở 15 nơi khác nhau:
const p1 = calculatePrice(100, "gold");
const p2 = calculatePrice(200, "silver");

Symptom: Product yêu cầu thêm khuyến mãi theo mùa (20% off). Junior phải sửa calculatePrice và kiểm tra 15 nơi gọi để đảm bảo không break.

Constraint: Không được thay đổi cách gọi ở 15 nơi ngay lập tức (quá nhiều file). Phải refactor từng bước.

Câu hỏi:

  1. Viết một HOF factory makePricer để tạo ra các hàm tính giá riêng biệt.
  2. Làm sao để dùng makePricer mà không phải sửa 15 chỗ gọi cũ ngay lập tức?
[Đáp án tham khảo]
  • Bạn nghĩ:

    1. js
      function makePricer(discountRate) {
        return function(price) {
          return Math.round(price * (1 - discountRate));
        };
      }
      const goldPrice = makePricer(0.2);
      const silverPrice = makePricer(0.1);
    2. Có thể wrap calculatePrice cũ để dùng factory bên trong:

      js
      function calculatePrice(price, type) {
        const pricers = {
          gold: makePricer(0.2),
          silver: makePricer(0.1),
          member: makePricer(0.05)
        };
        const pricer = pricers[type] || (p => p);
        return pricer(price);
      }

      Hoặc dần dần thay thế từng chỗ gọi:

      js
      // Thay vì:
      const p = calculatePrice(100, "gold");
      // Dùng:
      const goldPricer = makePricer(0.2);
      const p = goldPricer(100);
  • Bài học: HOF giúp tách "quy tắc giảm giá" thành đơn vị độc lập. Khi business rule thay đổi, bạn sửa factory một chỗ, không phải tìm kiếm toàn codebase.

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

Level B — Challenge

  1. Tự trả lời trước: Viết một đoạn code dùng HOF factory để tạo ra các hàm tính phí ship khác nhau theo vùng.
  2. Hỏi AI: "Give me an example of a higher-order function in JavaScript that returns a function."
  3. So sánh câu trả lời AI với code của bạn. AI có giải thích rõ tại sao việc trả về function hữu ích không? Hay chỉ đưa ví dụ?
  4. Verify bằng MDN: tìm "Higher-order functions" trên MDN và đọc phần định nghĩa.

Gợi ý

AI thường đưa ví dụ về Array.prototype.map (nhận function) khi được hỏi về HOF, và bỏ sót phía "trả về function" (factory). Nếu AI chỉ nói về callback mà không đề cập factory pattern, bạn đã tìm ra điểm mù phổ biến. HOF có hai mặt: nhận và trả về.

Đáp án tham khảo
  • Bạn nghĩ:

    js
    function makeShipper(baseFee) {
      return function(weight) {
        return baseFee + weight * 2;
      };
    }
    const urbanShipper = makeShipper(10);
    const ruralShipper = makeShipper(25);
  • AI trả lời (typical):

    • A higher-order function is a function that takes another function as an argument or returns a function.
    • Example: function multiply(x) { return function(y) { return x * y; }; }
    • This is useful for creating specialized functions.
  • So sánh: AI thường đúng về định nghĩa nhưng ví dụ factory thường đơn giản và không giải thích production value (tái sử dụng logic, giảm lặp code, tách config khỏi execution).

  • Điểm AI nói sai hoặc quá mơ hồ: "This is useful for creating specialized functions" — mơ hồ. Không giải thích khi nào nên dùng factory thay vì viết function thẳng. Không nhắc đến việc factory giúp tách "cấu hình" (mức thuế, phí ship) khỏi "thực thi" (tính toán với dữ liệu thực).

  • Kết luận: Nếu bạn chỉ ra được rằng HOF factory giúp tách config khỏi execution và scale khi có nhiều biến thể, 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 ở Stage 8 (React custom hooks chính là HOF factory) và Stage 12 (middleware architecture).

14. Teach Back (Dạy lại) ​

Giả sử một junior developer hỏi bạn:

"Em thấy makeTaxer(0.1)(100) chạy được. Tại sao có hai cặp ngoặc đơn? Cái thứ nhất trả về cái gì?"

Hãy giải thích trong 2 phút, dùng terminology: factory, returned function, specialization.

Mô phỏng
  • Bạn nói: makeTaxer(0.1) là gọi factory. Nó không trả về số tiền thuế. Nó trả về một function mới — một công cụ đã được cài sẵn thuế suất 10%. Cặp ngoặc thứ hai (100) là gọi công cụ đó với giá 100. Nếu bạn viết const taxer = makeTaxer(0.1), taxer chính là công cụ đó. Sau đó bạn có thể dùng đi dùng lại: taxer(100), taxer(200). Hai cặp ngoặc liền nhau chỉ là cách viết gọn: tạo công cụ rồi dùng ngay, không cần lưu vào biến.

💡 Tưởng tượng bạn đi thuê xe. makeTaxer(0.1) như thuê một chiếc xe đã đổ xăng. Cặp ngoặc thứ hai là bạn lái xe đi ngay. Bạn cũng có thể thuê xe về nhà để dùng dần — đó là gán vào biến const taxer = ....

Gợi ý đánh giá bản thân

  • Đồng nghiệp có hiểu tại sao không phải makeTaxer(0.1, 100) không?
  • Bạn có giải thích được sự khác biệt giữa "tạo function" và "chạy function" không?
  • Nếu đồng nghiệp hỏi: "Vậy taxer và makeTaxer khác nhau thế nào?" — bạn trả lời được không? (Gợi ý: makeTaxer là factory tạo ra taxer. taxer là sản phẩm đã được cấu hình.)

15. Assessment (Đánh giá) ​

Objective (Mục tiêu)Hình thức đánh giáTask
Recognize HOF (Nhận diện HOF)Prediction (Dự đoán)Câu 3 & 4, Prediction Exercise
Write function factory (Viết function factory)Implementation (Thực hành)Implementation Lab Level 1 & 2
Distinguish returned function vs result (Phân biệt function trả về và kết quả)Debug LabDebug Lab Section
Call returned function (Gọi function trả về từ HOF)Prediction + ImplementationCâu 3, Prediction Exercise + Implementation Lab Level 1
Explain factory benefit (Giải thích lợi ích factory)Teach BackTeach Back Section

16. Exit Criteria (Tiêu chí qua bài) ​

  • [ ] Có thể nhận diện một function là HOF dựa vào việc nó nhận function hoặc trả về function.
  • [ ] Có thể viết một function factory trả về function đã được cấu hình sẵn.
  • [ ] Có thể gọi function được trả về từ HOF: const fn = factory(); fn() hoặc factory()().
  • [ ] Có thể debug lỗi "validation always passes" do kiểm tra function thay vì kết quả của function.
  • [ ] Có thể phân biệt return fn (trả về function) và return fn() (trả về kết quả).
  • [ ] Có thể giải thích tại sao HOF factory giúp tách biệt config và execution.

17. Spiral Connection (Liên kết xoắn ốc) ​

Previous (Trước): Callback (0.5.5) — bạn đã biết function nhận function làm input. Giờ bạn học function trả về function làm output — hoàn thiện hình ảnh HOF.

Current (Hiện tại): Higher-order Functions — function nhận function, trả về function, hoặc cả hai. Factory pattern và function composition ở mức cơ bản.

Next (Tiếp theo):

  • 0.5.7 — Function Design: pure vs impure, side effect, single responsibility. HOF factory thường tạo ra pure function nếu không có side effect.
  • 0.7.4 — Defensive Programming: validation trong factory và returned function.
  • Stage 1 — Closure: cơ chế cho phép returned function "nhớ" biến từ scope ngoài. Đây là engine behavior đằng sau mọi HOF factory thực sự.
  • Stage 8 — React: custom hooks (useXxx) chính là HOF factory pattern áp dụng vào component lifecycle.
  • Stage 12 — Architecture: middleware pattern (app.use(middleware)) là HOF ở cấp hệ thống.
📴 Offline Mode — Content served from cache