Closure trong JavaScript
Bài viết này giải thích về closure trong JavaScript.
YouTube Video
Closure trong JavaScript
Trong JavaScript, 'closure' là một trong những khái niệm rất quan trọng và mạnh mẽ. Bằng cách hiểu về closures, bạn có thể tích lũy kiến thức hữu ích trong nhiều tình huống, như hành vi và phạm vi của hàm, cũng như xử lý bất đồng bộ và xử lý sự kiện trong JavaScript. Ở đây, chúng tôi sẽ giải thích chi tiết từ khái niệm cơ bản về closures cho đến các ví dụ cụ thể và ứng dụng của chúng.
Closure là gì?
Closure đề cập đến cơ chế mà một hàm có thể truy cập các biến trong phạm vi mà nó được tạo ra, ngay cả khi hàm đó được gọi bên ngoài phạm vi đó. Bằng cách sử dụng closures, các hàm có thể liên tục 'nhớ' các biến bên ngoài.
Closures bao gồm hai thành phần sau.
-
Định nghĩa hàm (bản thân hàm đó)
-
Phạm vi mà hàm được định nghĩa (các biến và hàm khác bên ngoài bản thân hàm đó)
Trong JavaScript, closures hoạt động vì các hàm có khả năng truy cập các biến trong phạm vi mà chúng được tạo ra.
Ví dụ cơ bản
Đầu tiên, hãy xem một ví dụ cơ bản về closure. Trong đoạn mã sau, outerFunction
trả về một hàm có tên là innerFunction
. Điểm quan trọng là innerFunction
có thể truy cập biến count
được định nghĩa trong phạm vi của outerFunction
.
1function outerFunction() {
2 let count = 0;
3
4 function innerFunction() {
5 count++;
6 console.log(`Current count: ${count}`);
7 }
8
9 return innerFunction;
10}
11
12const counter = outerFunction();
13counter(); // Current count: 1
14counter(); // Current count: 2
15counter(); // Current count: 3
Cách thức hoạt động của Closure
Như đã thấy trong ví dụ trên, count
vẫn được giữ lại bởi innerFunction
ngay cả sau khi outerFunction
đã được thực thi. innerFunction
có thể tiếp tục truy cập vào phạm vi của outerFunction
, nhờ đó count
được cập nhật bên trong innerFunction
. Đây là cơ chế cơ bản của một closure.
innerFunction
được gán cho biến counter
, và chúng ta có thể thấy rằng trạng thái của count
vẫn được lưu giữ mặc dù outerFunction
đã hoàn thành thực thi. Đây là bởi vì JavaScript liên tục 'ghi nhớ' phạm vi tại thời điểm định nghĩa hàm.
Ứng dụng: Closure như các biến riêng tư
Closure có thể được sử dụng giống như 'biến riêng tư' trong lập trình hướng đối tượng. Thông thường, trong JavaScript, các thuộc tính của đối tượng có thể truy cập trực tiếp từ bên ngoài, nhưng bằng cách sử dụng closures, ta có thể ngăn việc thao tác trực tiếp với biến trong phạm vi hàm từ bên ngoài.
Trong ví dụ tiếp theo, hàm createCounter
sử dụng closure để tạo một bộ đếm và trả về một bộ đếm với biến riêng tư count
.
1function createCounter() {
2 let count = 0;
3
4 return {
5 increment: function() {
6 count++;
7 console.log(`Count: ${count}`);
8 },
9 decrement: function() {
10 count--;
11 console.log(`Count: ${count}`);
12 },
13 getCount: function() {
14 return count;
15 }
16 };
17}
18
19const myCounter = createCounter();
20myCounter.increment(); // Count: 1
21myCounter.increment(); // Count: 2
22myCounter.decrement(); // Count: 1
23console.log(myCounter.getCount()); // 1
Trong ví dụ này, count
nằm trong phạm vi của hàm createCounter
, vì vậy không thể truy cập trực tiếp từ bên ngoài. Tuy nhiên, nó có thể được thao tác thông qua các phương thức increment
và decrement
. Bằng cách này, nhờ sử dụng closures, bạn có thể áp dụng khái niệm biến riêng tư trong JavaScript.
Các ví dụ thực tế về Closure
Kết hợp với các hàm callback
Closures thường được sử dụng kết hợp với các hàm callback để quản lý quá trình xử lý bất đồng bộ. Ví dụ, hãy cùng xem xét một ví dụ sử dụng bộ đếm thời gian (timer).
1function startTimer(duration) {
2 let timeLeft = duration;
3
4 function countdown() {
5 console.log(`Time left: ${timeLeft} seconds`);
6 timeLeft--;
7
8 if (timeLeft >= 0) {
9 setTimeout(countdown, 1000);
10 }
11 }
12
13 countdown();
14}
15
16startTimer(5);
17// Time left: 5 seconds
18// Time left: 4 seconds
19// Time left: 3 seconds
20// Time left: 2 seconds
21// Time left: 1 second
22// Time left: 0 seconds
Trong ví dụ này, hàm countdown
truy cập biến timeLeft
trong phạm vi của startTimer
. Bằng cách này, closures rất hữu ích cho việc xử lý bất đồng bộ như các bộ đếm thời gian, vì chúng duy trì trạng thái của biến theo thời gian.
Trình xử lý sự kiện
Closures cũng rất tiện lợi khi thiết lập trình xử lý sự kiện. Trong ví dụ sau, closure được sử dụng để ghi lại số lần một nút được nhấn.
1function setupClickCounter(buttonId) {
2 let clickCount = 0;
3
4 const button = document.getElementById(buttonId);
5 button.addEventListener('click', function() {
6 clickCount++;
7 console.log(`Button clicked ${clickCount} times`);
8 });
9}
10
11setupClickCounter('myButton');
Trong trường hợp này, clickCount
tăng lên mỗi lần nhấn và giá trị đó được giữ lại. Bằng cách sử dụng closures, bạn có thể gán một bộ đếm độc lập cho từng nút.
Kết luận
Closure là một khái niệm tượng trưng cho sự linh hoạt và mạnh mẽ của JavaScript. Chúng lưu giữ các biến được bao bọc trong phạm vi hàm và cho phép thao tác trên những biến đó thông qua các hàm có thể truy cập từ bên ngoài. Bằng cách hiểu và ứng dụng cơ chế này, bạn có thể trau dồi các kỹ thuật lập trình JavaScript nâng cao hơn.
Closures được áp dụng trong nhiều tình huống khác nhau, từ xử lý sự kiện và bất đồng bộ cho đến cả các mô phỏng lập trình hướng đối tượng.
Bạn có thể làm theo bài viết trên bằng cách sử dụng Visual Studio Code trên kênh YouTube của chúng tôi. Vui lòng ghé thăm kênh YouTube.