Closures là một thứ khá là khó để nắm bắt trong Javascript. Tuy vậy nó lại rất quan trọng nếu như bạn muốn trở thành một Javascript Developer.
Việc hiểu rõ về closures sẽ giúp cho code của bạn trở lên “thanh lịch” hơn và vì nó là một thứ quan trọng nên là việc bạn hiểu rõ nó sẽ giúp cho bạn trong các cuộc phỏng vấn về Javascript.
BONUS: Thật ra thì closures không phải là khái niệm riêng của Javascript. Đây là một khái niệm chung trong khoa học máy tính/lập trình vì vậy mà bạn có thể dễ dàng tìm thấy ở mọi nơi, mọi ngôn ngữ lập trình hiện đại.
Nội dung bài viết
Functions Are Values Too
Đầu tiên, bạn cần nhiểu rằng là Javascript hỗ trợ first-class functions. (Bạn có thể đọc về first-class function tại đây)

Nghe tên thì có vẻ khá là lạ và cao siêu đúng không. Nhưng nó đơn giản chỉ là function được sử dụng giống như là bất cứ các loại giá trị nào khác. Các loại ở đây là string, number hay object.
Các giá trị được gán vào biến
const name = 'Yazeed';
const age = 25;
const fullPerson = {
name: name,
age: age
};
Các giá trị được cho vào array
const items = [
'Yazeed',
25,
{ name: 'Yazeed', age: 25 }
]
Các giá trị có thể được trả về từ các function
function getPerson() {
return [
'Yazeed',
25,
{ name: 'Yazeed', age: 25 }
];
}
Và đoán xem, function cũng có thể như trên.

Function có thể gán vào biến
const sayHi = function(name) {
return `Hi, ${name}!`;
}
Function được cho vào array
const myFunctions = [
function sayHi(name) {
return `Hi, ${name}!`;
},
function add(x, y) {
return x + y;
}
];
Và một điều thú vị đây này…
Function có thể được trả về từ các function
Một hàm mà trả về một hàm khác có một tên khá đặc biệt. Nó được gọi là hàm bậc cao (higher-order). Đây chính là nền tảng của closure đó, chúng ta cùng xét ví dụ này nhé:
function getGreeter() {
return function() {
return 'Hi, Sunners!';
};
}
Bạn có thể thấy rằng là getGreeter
trả về một function khác bên trong. Vì vậy để được chào đón, hãy gọi nó hai lần như sau:
getGreeter(); // Returns function
getGreeter()(); // Hi, Sunners!
Một là để trả về function bên trong, còn cái sau đó chính là để lấy về giá trị cuối cùng của function bên trong đó.
Bạn cũng có thể dễ dàng khởi tạo nó thành một biến để có thể sử dụng:
const greetJerome = getGreeter();
greetJerome(); // Hi, Jerome!
greetJerome(); // Hi, Jerome!
greetJerome(); // Hi, Jerome!
Kết hợp cùng tham số
Thay vì fix cứng từ ‘Sunners’ thì chúng ta có thể truyền tham số để nó chào người khác.
// We can greet anyone now!
function getGreeter(name) {
return function() {
return `Hi, ${name}!`;
};
}
Và khi sử dụng:
const greetJerome = getGreeter('Jerome');
const greetYazeed = getGreeter('Yazeed');
greetJerome(); // Hi, Jerome!
greetYazeed(); // Hi, Yazeed!
Nhìn vào ví dụ trên bạn có thể thấy rằng param name
là của thằng function cha. Tuy vậy thằng function con bên trong cũng có thể sử dụng được nhé. Đây chính là sức mạnh của closures đó.
Lợi ích của việc sử dụng Closures

- Data Privacy
Data Privacy chính là việc tránh cho các thành phần bên ngoài thao tác đến các biến bên trong function.
Ví dụ: Ngân hàng không có Data Privacy
Hãy xem xét đoạn code dưới đây là để quản lý một tài khoản ngân hàng.
let accountBalance = 0;
const manageBankAccount = function() {
return {
deposit: function(amount) {
accountBalance += amount;
},
withdraw: function(amount) {
// ... safety logic
accountBalance -= amount;
}
};
}
Tuy vậy accountBalance
hoàn toàn có thể bị thay đổi giá trị từ bên ngoài: // later in the script…
accountBalance = 'Whatever I want, muhahaha >:)';

Các ngôn ngữ như Java và C++, PHP,… cho phép các lớp có các property riêng (protected, private) . Các property này không thể được truy cập bên ngoài lớp.
JavaScript không hỗ trợ các biến riêng tư, nhưng chúng ta có thể sử dụng các closures!
const manageBankAccount = function(initialBalance) {
let accountBalance = initialBalance;
return {
getBalance: function() { return accountBalance; },
deposit: function(amount) { accountBalance += amount; },
withdraw: function(amount) {
if (amount > accountBalance) {
return 'You cannot draw that much!';
}
accountBalance -= amount;
}
};
}
Và có lẽ sử dụng nó như vậy …
const accountManager = manageBankAccount(0);
accountManager.deposit(1000);
accountManager.withdraw(500);
accountManager.getBalance(500);
Và bây giờ thì bất cứ cái gì cũng ko thể thao tác đến accountBalance
một cách trực tiếp mà phải thông qua deposit
hay là withdraw
.

- Currying
Currying là gì thì bạn có thể xem chi tiết ở đây nhé. Tìm hiểu Higher-Order Function (HOF) và Currying qua một số ví dụ
Thay vì:
const add = function(x, y) {
return x + y;
}
add(2, 4); // 6
Bạn có thể chuyển thành currying như sau:
const add = function(x) {
return function(y) {
return x + y;
}
}
Và bây giờ để sử dụng:
const add10 = add(10);
add10(10); // 20
add10(20); // 30
add10(30); // 40
Summary
- Function có thể dùng như các kiểu dữ liệu khác.
- Function có thể return function khác.
- Các biến của hàm cha có thể được sử dụng ở trong các hàm con.
- Các biến này được gọi là state.
Bài viết được dịch từ: https://www.freecodecamp.org/news/learn-javascript-closures-in-n-minutes/