Functional Programming — Tư Duy Mới Cho Code Sạch Hơn
Khái niệm functional programming: pure function, immutability, higher-order function, monad — áp dụng trong JavaScript.
Functional programming (FP) là paradigm coi tính toán như evaluation của function, tránh state và mutable data. Không chỉ dành cho Haskell — áp dụng FP trong JS/TS giúp code dễ đoán, dễ test.
Pure Function — Hàm Thuần Khiết#
// ❌ Impure — phụ thuộc external state
let taxRate = 0.1;
function calculatePrice(price: number) {
return price + price * taxRate; // taxRate có thể thay đổi
}
// ✅ Pure — cùng input → cùng output, không side effect
function calculatePrice(price: number, taxRate: number) {
return price + price * taxRate;
}typescriptLợi ích: Dễ test (không cần mock), dễ cache (memoization), dễ debug.
Immutability — Không Thay Đổi#
// ❌ Mutate trực tiếp
const user = { name: 'Alice', age: 25 };
user.age = 26; // Mutate!
// ✅ Immutable — tạo object mới
const updatedUser = { ...user, age: 26 };
// Với array
const numbers = [1, 2, 3];
// ❌ Mutate
numbers.push(4);
// ✅ Immutable
const newNumbers = [...numbers, 4];
// hoặc
const newNumbers = numbers.concat(4);typescriptImmer — thư viện giúp immutable dễ dàng:
import { produce } from 'immer';
const state = { users: [{ id: 1, name: 'Alice' }] };
const nextState = produce(state, (draft) => {
draft.users[0].name = 'Bob';
});
// state không thay đổi, nextState là object mớitypescriptHigher-Order Function — Hàm Bậc Cao#
Function nhận function làm tham số hoặc trả về function:
// HOF — trả về function
function createLogger(prefix: string) {
return (message: string) => {
console.log(`[${prefix}] ${message}`);
};
}
const infoLogger = createLogger('INFO');
const errorLogger = createLogger('ERROR');
infoLogger('Server started'); // [INFO] Server started
errorLogger('Connection failed'); // [ERROR] Connection failedtypescriptMap, Filter, Reduce — Ba Trụ Cột#
const numbers = [1, 2, 3, 4, 5];
// Map — biến đổi từng phần tử
const doubled = numbers.map(n => n * 2);
// [2, 4, 6, 8, 10]
// Filter — lọc
const even = numbers.filter(n => n % 2 === 0);
// [2, 4]
// Reduce — gom về một giá trị
const sum = numbers.reduce((acc, n) => acc + n, 0);
// 15typescriptVí dụ Thực Tế#
interface Order {
id: number;
amount: number;
status: 'pending' | 'paid' | 'cancelled';
}
const orders: Order[] = [
{ id: 1, amount: 100, status: 'paid' },
{ id: 2, amount: 200, status: 'pending' },
{ id: 3, amount: 300, status: 'paid' },
];
// Tính tổng amount của đơn đã paid
const totalPaid = orders
.filter(o => o.status === 'paid')
.map(o => o.amount)
.reduce((acc, amount) => acc + amount, 0);
// 400
// Thay vì dùng for loop + iftypescriptFunction Composition — Kết Hợp Function#
const toUpperCase = (s: string) => s.toUpperCase();
const exclaim = (s: string) => `${s}!`;
const repeat = (s: string) => `${s} ${s}`;
// Composition thủ công
const shout = (s: string) => repeat(exclaim(toUpperCase(s)));
// Với compose
const compose = (...fns: Function[]) =>
(x: any) => fns.reduceRight((acc, fn) => fn(acc), x);
const shout = compose(repeat, exclaim, toUpperCase);
console.log(shout('hello')); // HELLO! HELLO!
// Với pipe (ngược chiều)
const pipe = (...fns: Function[]) =>
(x: any) => fns.reduce((acc, fn) => fn(acc), x);
const shout = pipe(toUpperCase, exclaim, repeat);typescriptCurry — Function Có Sẵn Tham Số#
// Curry — function nhận từng tham số một
const multiply = (a: number) => (b: number) => a * b;
const double = multiply(2);
const triple = multiply(3);
console.log(double(5)); // 10
console.log(triple(5)); // 15typescriptMaybe — Xử Lý Null An Toàn#
class Maybe<T> {
private constructor(private value: T | null) {}
static of<T>(value: T | null | undefined): Maybe<T> {
return new Maybe(value ?? null);
}
map<U>(fn: (value: T) => U): Maybe<U> {
if (this.value === null) return Maybe.of(null);
return Maybe.of(fn(this.value));
}
getOrElse(defaultValue: T): T {
return this.value ?? defaultValue;
}
}
// Usage
const user = Maybe.of(getUserFromDB(1))
.map(u => u.address)
.map(a => a.city)
.getOrElse('Unknown');
// Không cần if (user && user.address && user.address.city)typescriptLợi Ích Của FP#
| Vấn đề | OOP | FP |
|---|---|---|
| State | Object mutation | Immutability |
| Side effects | Method gọi method | Pure functions |
| Error handling | try/catch | Either, Maybe |
| Loops | for, while | map, filter, reduce |
| Dependencies | DI, mock | Pure function, no mock needed |
FP Trong React#
React khuyến khích FP:
// Component là pure function (lý tưởng)
function UserCard({ user }: { user: User }) {
return (
<div>
<h2>{user.name}</h2>
<p>{user.email}</p>
</div>
);
}
// useReducer + dispatch → reducer là pure function
// Immer + useState → immutable statetsxKết Luận#
Bạn không cần chuyển hoàn toàn sang FP — chỉ cần áp dụng một số nguyên tắc:
- Pure functions cho logic business — dễ test
- Immutability — tránh bug khó lường
- map/filter/reduce — thay for loop
- Function composition — code nhỏ, kết hợp linh hoạt
FP làm code dễ hiểu, dễ kiểm tra, dễ bảo trì. Không phải “tất cả hoặc không” — hãy áp dụng dần.