Skip to content
Home
Go back

Higher-Order Function trong JavaScript

Mục lục

Xem mục lục

Higher-Order Function là gì

Higher-Order Function (HOF) là một function thỏa ít nhất một trong hai điều kiện: nhận function khác làm tham số, hoặc trả về một function. Nhiều HOF thỏa cả hai cùng lúc.

function applyTwice(fn, value) {
  return fn(fn(value));
}

applyTwice(x => x * 2, 3); // 12

applyTwice là HOF vì nhận fn làm tham số. Function truyền vào (x => x * 2) thường được gọi là callback.

First-class Function, điều kiện tiên quyết

HOF chỉ tồn tại được trong ngôn ngữ coi function là first-class citizen, tức function được đối xử như bất kỳ giá trị nào khác: gán được cho biến, lưu được trong mảng/object, truyền được làm tham số, và trả về được từ một function khác.

const greet = function () {
  console.log("Xin chào");
};

const fns = [greet, () => console.log("Tạm biệt")];

fns[0](); // "Xin chào"

JavaScript hỗ trợ first-class function ngay từ đầu, đây là nền tảng khiến các pattern như HOF, callback, closure đều khả thi. Ở một ngôn ngữ không coi function là first-class, muốn truyền hành vi tùy chỉnh phải dùng cách gián tiếp hơn nhiều, ví dụ định nghĩa hẳn một interface hoặc class riêng cho việc đó.

Các HOF có sẵn quen thuộc

Nhiều method có sẵn trên Array.prototype là HOF, vì nhận callback làm tham số: map, filter, reduce, forEach, sort, find.

const numbers = [1, 2, 3, 4, 5];

const doubled = numbers.map(n => n * 2); // [2, 4, 6, 8, 10]
const evens = numbers.filter(n => n % 2 === 0); // [2, 4]
const sum = numbers.reduce((acc, n) => acc + n, 0); // 15

So với vòng lặp for thuần làm cùng việc:

const doubled = [];
for (let i = 0; i < numbers.length; i++) {
  doubled.push(numbers[i] * 2);
}

map gói gọn phần “lặp qua mảng” bên trong, chỉ để lộ ra phần logic biến đổi từng phần tử (n => n * 2). Code ngắn hơn, và ý định (biến đổi từng phần tử thành giá trị mới) rõ ràng hơn so với đọc một vòng lặp thủ công.

Function trả về Function

Chiều còn lại của HOF: một function trả về function khác, thường được gọi là function factory.

function multiply(a) {
  return function (b) {
    return a * b;
  };
}

const double = multiply(2);
const triple = multiply(3);

double(5); // 10
triple(5); // 15

multiply trả về một function mới mỗi lần gọi, function đó “nhớ” giá trị a đã truyền vào nhờ closure. Đây là chỗ HOF và closure thường đi cùng nhau: HOF cung cấp cơ chế trả về function, closure cung cấp cơ chế giữ lại trạng thái bên trong function được trả về.

Ứng dụng thực tế

Compose / Pipe

Kết hợp nhiều function nhỏ thành một function lớn, dữ liệu chảy qua từng bước:

const pipe =
  (...fns) =>
  input =>
    fns.reduce((acc, fn) => fn(acc), input);

const process = pipe(
  x => x + 1,
  x => x * 2,
  x => x - 3
);

process(5); // ((5 + 1) * 2) - 3 = 9

Decorator

Bọc thêm hành vi quanh một function có sẵn mà không sửa function gốc:

function withLogging(fn) {
  return function (...args) {
    console.log(`Gọi ${fn.name} với`, args);
    return fn(...args);
  };
}

const add = (a, b) => a + b;
const loggedAdd = withLogging(add);

loggedAdd(2, 3); // log ra tham số, rồi trả về 5

Middleware kiểu Express

Middleware trong Express là một dạng HOF: mỗi middleware nhận req, res, next (chính next cũng là một function được truyền vào), tạo thành chuỗi các function gọi lẫn nhau.

app.use((req, res, next) => {
  console.log(`${req.method} ${req.url}`);
  next();
});

Đánh đổi cần lưu ý

HOF giúp code ngắn gọn và tách bạch rõ phần logic lặp/điều khiển khỏi phần logic nghiệp vụ, nhưng có vài đánh đổi:

Tổng kết


Share this post:

Previous Post
TypeScript: Interface và Type khác nhau ở đâu
Next Post
Scope trong JavaScript: Global, Function và Block