Closure là một khái niệm không phải ai cũng biết và thực sự hiểu về nó, đây có thể coi là một cách định nghĩa hàm nâng cao giúp code nhìn trong sáng, cách sử dụng linh hoạt hơn, vì vậy nếu bạn muốn học Javascript nâng cao hoặc học các Javascript Framework khác thì bắt buộc phải hiểu về Closure. Vậy Closure là gì thì chúng ta cùng tìm hiểu nhé.
1. Closure function là gì?
Closure là một hàm được tạo ra từ bên trong một hàm khác (hàm cha), nó có thể sử dụng các biến toàn cục, biến cục bộ của hàm cha và biến cục bộ của chính nó. Việc viết hàm theo kiểu closure trong một số trường hợp sẽ giúp code nhìn sáng và dễ quản lý hơn, linh hoạt hơn trong việc xử lý các chức năng.
Ví dụ: Xây dựng hàm closure hiển thị thông báo với câu thông báo được nhận từ một tham số, sau đó nối thêm chữ code24h.com đằng sau chuỗi đó.
1 2 3 4 5 | function showMessage(message){ alert(message + ' - Freetuts.net' ); } showMessage( 'Xin chào các bạn' ); |
Cách viết này rất đơn giản và dễ hiểu. Nhưng bây giờ mình muốn viết một dạng khác nhìn rối hơn chút xíu đó là cách sử dụng closure function.
XEM DEMO
1 2 3 4 5 6 7 8 9 10 11 12 13 | // Bước 1: Tạo hàm closure function showMessage(message) { return function (){ alert(message + ' - Freetuts.net' ); }; } // Bước 2: khởi tạo hàm closure var messageFunc = showMessage( 'Xin chào các bạn' ); // Bước 3: Chạy hàm closure messageFunc(); |
Rõ ràng nhìn cách này rất rối, khó hiểu nhất là tại bước 2, vì vậy mình sẽ giải thích bước này nhé. Bạn để ý trong hàm showMessage mình đã return về một function, vì vậy lúc khởi tạo và gán hàm nó vào biến messageFunc thì biến messageFunc sẽ là một #function chưa được khởi động, vì vậy câu thông báo sẽ chưa xuất hiện. Đoạn code ở bước 3 sẽ khởi động hàm trả về đó và câu thông báo sẽ xuất hiện.
2. Các ví dụ hàm Closure trong Javascript
Cách viết hàm Closure rất linh động, nó phụ thuộc vào bài toán cụ thể mà đưa ra giải pháp khác nhau. Và sau đây mình sẽ đưa ra những ví dụ về một số cách viết thông thường.
Closure có tham số
Trong ví dụ ở phần một mình tạo một closure không có tham số, vậy nếu trường hợp có tham số thì cách viết như thế nào? Bạn xem ví dụ dưới đây sẽ hiểu.
XEM DEMO
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | // Bước 1: Tạo hàm closure function showMessage(message) { return function (time){ for ( var i = 1; i = time; i++){ alert(message + ' - Freetuts.net' ); } }; } // Bước 2: khởi tạo hàm closure var messageFunc = showMessage( 'Xin chào các bạn' ); // Bước 3: Chạy hàm closure messageFunc(2); |
Trong ví dụ này hàm closure trả về có một tham số time, lúc này tại bước 3 ta phải truyền nó vào thì chương trình mới hoạt động bình thường.
Closure thay đổi giá trị biến toàn cục lẫn cục bộ
Các closure function có thể sử dụng biến ở 3 phạm vi, thứ nhất là biến toàn cục, thứ hai là biến cục bộ của hàm cha và thứ ba là biến cục bộ của chính nó. Không chỉ sử dụng được mà còn có khả năng thay đổi giá trị của các biến đó.
XEM DEMO
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | // Bước 1: Tạo hàm closure function Student() { var name = '; var age = ' ; return { set : function (in_name, in_age){ name = in_name; age = in_age; }, getName : function (){ return name; }, getAge : function (){ return age; } }; } // Bước 2: khởi tạo hàm closure var studentObj = Student(); // Bước 3: Chạy hàm closure studentObj.set( 'Nguyễn Văn Cường' , '27' ); alert(studentObj.getName()); alert(studentObj.getAge()); |
Return nhiều Closure Function
Nếu bạn muốn return nhiều hàm closure thì bạn phải sử dụng một object, trong đó mỗi phần tử sẽ là một closure function.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | function multiClosure() { return { func1 : function (){ console.log( 'Closure1' ); }, func2 : function (){ console.log( 'Closure2' ); } }; } // Cách sử dụng var object = multiClosure(); object.func1(); object.func2(); |
Độ ưu tiên các biến
Như ta biết closure function có thể sử dụng biến tại ba vị trí đó là biến toàn cục, biến cục bộ hàm cha và biến cục bộ của chính nó. Bây giờ có một trường hợp là tên các biến ở ba vị trí đó bị trùng nhau thì sẽ có chuyện gì xảy ra? Trường hợp này nó sẽ ưu tiên từ trong ra ngoài, nghĩa là nó sẽ thực hiện các bước như sau:
- Bước 1: Xem biến cục bộ trong hàm closure có trùng không? Nếu không trùng thì nó sẽ qua bước 2, còn nếu có trùng thì nó sẽ coi biến đó là biến cục bộ của chính nó.
- Bước 2: Xem biến cục bộ của hàm cha có trùng không? Nếu không trùng thì qua bước 3, nếu trùng thì nó sẽ coi biến đó là biến cục bộ của hàm cha.
- Bước 3: Xem biến toàn cục có trùng không? Nếu không trùng thì nó sẽ khởi tạo mới và đó sẽ là biến cục bộ của hàm closure, nếu trùng thì nó sẽ coi biến đó là biến toàn cục.
Xem ví dụ sau:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | // Bước 1: Tạo hàm closure var message = 'Biên toàn cục' ; function showMessage() { var message = 'Biến cục bộ của hàm cha' ; return function (){ alert(message); }; } // Bước 2: khởi tạo hàm closure var messageFunc = showMessage(); // Bước 3: Chạy hàm closure messageFunc(); |
Trong ví dụ này biến message trong hàm closure sẽ lấy giá trị của biến cục bộ hàm cha.
3. Lời kết
Qua bài này mình đã giải thích sơ lược về khái niệm Closure là gì, đồng thời cũng đưa ra một số ví dụ rất căn bản về cách sử dụng hàm Closure trong javascript. Bài này mình chỉ tập trung giải thích về lý thuyết nên các ví dụ đưa ra vẫn chưa thực sự chuyên sâu, hy vọng sẽ giúp ích cho các bạn.