Closure là một khái niệm quan trọng trong JavaScript và là nền tảng của nhiều tính năng mạnh mẽ trong ngôn ngữ này. Closure cho phép bạn tạo ra các hàm có khả năng "nhớ" môi trường mà chúng được tạo, ngay cả khi môi trường đó không còn tồn tại.
Closure là một hàm có thể truy cập vào các biến từ phạm vi bên ngoài (outer scope), ngay cả khi hàm đó đã được thực thi hoặc môi trường bên ngoài đã kết thúc vòng đời.
JavaScript sử dụng cơ chế Lexical Scoping (phạm vi tĩnh) để xác định nơi một biến có thể được truy cập. Khi một hàm được tạo ra, nó sẽ "nhớ" tất cả các biến từ phạm vi chứa nó, kể cả sau khi hàm bên ngoài đã hoàn thành.
function outerFunction() {
let outerVariable = "I'm outside!";
function innerFunction() {
console.log(outerVariable); // Truy cập biến từ phạm vi bên ngoài
}
return innerFunction;
}
const closure = outerFunction(); // Gán innerFunction vào biến closure
closure(); // Output: I'm outside!
Trong ví dụ trên:
innerFunction
là một closure vì nó truy cập biến outerVariable
từ phạm vi của outerFunction
.outerFunction
đã hoàn thành, innerFunction
vẫn có thể truy cập outerVariable
.Closure rất hữu ích trong nhiều tình huống khác nhau:
JavaScript không hỗ trợ khai báo private variables trực tiếp trong class (trước ES6). Tuy nhiên, bạn có thể sử dụng closure để tạo các biến riêng tư.
function createCounter() {
let count = 0; // Biến private
return {
increment: function () {
count++;
console.log(count);
},
decrement: function () {
count--;
console.log(count);
}
};
}
const counter = createCounter();
counter.increment(); // Output: 1
counter.increment(); // Output: 2
counter.decrement(); // Output: 1
Ở đây:
count
không thể truy cập trực tiếp từ bên ngoài.count
thông qua các phương thức increment
và decrement
.Closure có thể được sử dụng để tạo các factory functions, tức là các hàm trả về các hàm khác với các hành vi cụ thể.
function createMultiplier(multiplier) {
return function (value) {
return value * multiplier;
};
}
const double = createMultiplier(2);
const triple = createMultiplier(3);
console.log(double(5)); // Output: 10
console.log(triple(5)); // Output: 15
Trong ví dụ trên:
createMultiplier
trả về một closure ghi nhớ giá trị của multiplier
.createMultiplier
, một closure mới được tạo với giá trị multiplier
khác nhau.Closure thường được sử dụng trong callback functions để duy trì trạng thái giữa các lần gọi.
function setupCounter() {
let count = 0;
document.getElementById("incrementBtn").addEventListener("click", function () {
count++;
console.log(`Count: ${count}`);
});
}
setupCounter();
Trong ví dụ trên:
count
giữa các lần nhấn nút.Closure là nền tảng của module pattern, giúp tổ chức mã nguồn theo kiểu mô-đun, tránh xung đột tên biến và tạo private/public API.
const MyModule = (function () {
let privateVariable = "I am private";
function privateMethod() {
console.log(privateVariable);
}
return {
publicMethod: function () {
privateMethod();
}
};
})();
MyModule.publicMethod(); // Output: I am private
console.log(MyModule.privateVariable); // Output: undefined
Mặc dù closure rất mạnh mẽ, nhưng cần lưu ý một số vấn đề:
Closure có thể giữ lại các biến trong bộ nhớ ngay cả khi chúng không còn cần thiết, dẫn đến memory leaks nếu không quản lý đúng cách.
function createBigObject() {
let bigArray = new Array(1000000).fill("data"); // Tạo một mảng lớn
return function () {
console.log(bigArray.length);
};
}
const closure = createBigObject();
closure(); // Output: 1000000
Trong ví dụ trên:
bigArray
vẫn chiếm bộ nhớ ngay cả khi createBigObject
đã hoàn thành.Closure có thể làm giảm hiệu suất nếu được sử dụng quá mức trong các vòng lặp hoặc ứng dụng đòi hỏi tốc độ cao.
function createFunctions() {
const result = [];
for (var i = 0; i < 5; i++) {
result.push(function () {
console.log(i);
});
}
return result;
}
const functions = createFunctions();
functions[0](); // Output: 5
functions[1](); // Output: 5
Trong ví dụ trên:
i
, và giá trị cuối cùng của i
là 5
.let
thay vì var
hoặc wrap closure trong một IIFE.Closure giúp tránh việc sử dụng global scope, giúp mã nguồn sạch hơn và dễ bảo trì hơn.
// Sử dụng global scope
let counter = 0;
function increment() {
counter++;
console.log(counter);
}
// Sử dụng closure
function createCounter() {
let counter = 0;
return function () {
counter++;
console.log(counter);
};
}
const increment = createCounter();
increment(); // Output: 1
increment(); // Output: 2
Closure được sử dụng rộng rãi trong các thư viện và framework JavaScript, chẳng hạn như:
function debounce(func, delay) {
let timeoutId;
return function (...args) {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => func.apply(this, args), delay);
};
}
const handleInput = debounce((value) => {
console.log("Input:", value);
}, 300);
document.getElementById("inputField").addEventListener("input", (e) => {
handleInput(e.target.value);
});
Closure là một khái niệm mạnh mẽ trong JavaScript, cho phép bạn:
Tuy nhiên, cần lưu ý các vấn đề về bộ nhớ và hiệu suất khi sử dụng closure. Nếu được sử dụng đúng cách, closure sẽ giúp bạn viết mã nguồn gọn gàng, linh hoạt và dễ bảo trì hơn.