Trong hành trình học JavaScript, có một khái niệm mà hầu như ai cũng từng nghe qua – Closure. Nó xuất hiện trong các bài phỏng vấn, trong các đoạn code thực tế, và trong các tài liệu học thuật. Nhưng bạn có bao giờ tự hỏi: “Mình đã thực sự hiểu closure chưa?” Hay bạn chỉ biết nó là “hàm lồng trong hàm” và có thể “nhớ biến bên ngoài”?
Bài viết này sẽ giúp bạn hiểu rõ bản chất của closure, cách nó hoạt động, ứng dụng thực tế, và những hiểu lầm phổ biến mà lập trình viên thường gặp.
1. Closure Là Gì?
Closure là khả năng của một hàm ghi nhớ và truy cập đến các biến trong phạm vi mà nó được khai sinh, ngay cả khi phạm vi đó đã kết thúc.
function outer() {
let count = 0;
function inner() {
count++;
console.log(count);
}
return inner;
}
const fn = outer();
fn(); // 1
fn(); // 2
Ở đây, inner() là một closure. Nó vẫn có thể truy cập biến count của outer() dù outer() đã kết thúc.
2. Vì Sao Closure Quan Trọng?
- Tạo hàm riêng biệt với trạng thái riêng.
- Ẩn thông tin (encapsulation) – giống như biến private.
- Xây dựng các hàm callback, event handler.
- Tối ưu hiệu suất và bộ nhớ.
- Tạo ra các factory function, currying, memoization.
3. Cách Closure Hoạt Động Trong JavaScript
Khi một hàm được khai báo, JavaScript lưu lại phần thân hàm và cả phạm vi lexical nơi nó được tạo ra. Khi hàm được gọi, nó sẽ tìm biến theo thứ tự:
- Trong chính nó.
- Trong phạm vi bên ngoài (closure).
- Trong phạm vi toàn cục.
4. Các Trường Hợp Closure Thường Gặp
a. Callback trong setTimeout
function greet(name) {
setTimeout(function () {
console.log("Hello " + name);
}, 1000);
}
greet("Tien");
b. Tạo biến private
function Counter() {
let count = 0;
return {
increment: function () {
count++;
return count;
},
decrement: function () {
count--;
return count;
}
};
}
const counter = Counter();
console.log(counter.increment()); // 1
console.log(counter.decrement()); // 0
c. Currying
function multiply(a) {
return function (b) {
return a * b;
};
}
const double = multiply(2);
console.log(double(5)); // 10
5. Những Hiểu Lầm Phổ Biến Về Closure
- Closure là một tính năng đặc biệt: Không hẳn. Nó là hệ quả tự nhiên của cách JavaScript xử lý phạm vi và hàm.
- Closure chỉ dùng trong các hàm lồng nhau: Không đúng. Closure có thể xảy ra bất kỳ khi nào một hàm được trả về hoặc truyền đi mà vẫn giữ được tham chiếu đến phạm vi ban đầu.
- Closure gây rò rỉ bộ nhớ: Closure không tự gây rò rỉ bộ nhớ, nhưng nếu bạn giữ tham chiếu đến các biến không cần thiết thì có thể gây ra vấn đề.
6. Closure Trong ES6 Và Arrow Function
Arrow function cũng tạo closure, nhưng có một điểm khác biệt: không có this riêng, nó lấy this từ phạm vi bên ngoài.
function Timer() {
this.seconds = 0;
setInterval(() => {
this.seconds++;
console.log(this.seconds);
}, 1000);
}
new Timer();
7. Closure Trong Thực Tế – Dự Án React/Next.js
✅ Trong useEffect, useCallback, useMemo
useEffect(() => {
const timer = setInterval(() => {
console.log("Tick");
}, 1000);
return () => clearInterval(timer);
}, []);
✅ Trong custom hooks
function useCounter() {
const [count, setCount] = useState(0);
const increment = useCallback(() => {
setCount((prev) => prev + 1);
}, []);
return { count, increment };
}
8. Làm Sao Để Thành Thạo Closure?
- Thực hành nhiều: Viết các hàm có trạng thái riêng, thử tạo biến private, thử viết currying.
- Đọc mã nguồn thư viện: Xem cách các thư viện như Redux, React, Lodash sử dụng closure.
- Giải thích lại cho người khác: Nếu bạn có thể giải thích closure cho người khác hiểu, bạn đã thực sự hiểu nó.
9. Kết Luận
Closure không phải là một “bí thuật” trong JavaScript. Nó là một phần cốt lõi của ngôn ngữ, giúp bạn viết code linh hoạt, tối ưu và có tổ chức hơn.
Nếu bạn là lập trình viên JavaScript, việc hiểu rõ closure sẽ giúp bạn:
- Viết code tốt hơn.
- Hiểu sâu hơn về React, Next.js, Node.js.
- Tự tin hơn khi phỏng vấn.
- Tránh được các lỗi khó chịu liên quan đến phạm vi và trạng thái.
Vậy nên, hãy dành thời gian để thực sự hiểu closure – không chỉ biết nó là gì, mà còn biết khi nào nên dùng, vì sao nên dùng, và dùng như thế nào cho đúng.

