Bài viết này mình sẽ giới thiệu với bạn về JavaScript closures là gì. Việc hiểu closures sẽ giúp bạn viết code tốt hơn (ngắn gọn, súc tích hơn và thậm chí dễ hiểu hơn,…).
Tuy nhiên, trước khi đi vào những ưu, nhược điểm của việc sử dụng closures, chúng ta sẽ xem JavaScript closures là gì và cách sử dụng nó như thế nào nhé!
Cùng bắt đầu nào!
JavaScript closures là gì?
JavaScript Closures là tập hợp bao gồm một hàm và môi trường nơi hàm số đó được khai báo. Ở đây, môi trường bao gồm tất cả những biến cục bộ trong phạm vi hàm số được khai báo. (Để đơn giản, từ nay mình sẽ sử dụng hàm closures khi nói về JavaScript closures).
Hàm closures có thể truy cập biến số ở 3 phạm vi khác nhau là:
- Biến toàn cục (global)
- Biến được khai báo ở hàm số chứa hàm closures (outer function)
- Biến ở trên trong hàm closures
Ví dụ:
function adder(n){ var intro = 'This answer is '; var local = n; return function(number){ var result = number + local; console.log(intro + result); } } var adder2 = adder(2); adder2(10); // => This answer is 12
Trong ví dụ trên, hàm closures là một hàm số không tên function (number). Hàm closures này sử dụng biến cục bộ của outer function là intro và local.
Khi mình gọi hàm adder(2), hàm số này thực hiện và kết quả trả về được gán vào biến adder2. Khi gọi adder2(10), kết quả trả về là 12, chứng tỏ hàm closures vẫn có thể truy cập tới biến cục bộ của outer function khi hàm đó thực hiện xong.
Hàm closures lưu trữ biến số của outer function theo kiểu tham chiếu
Xét ví dụ dưới đây:
function ObjId(){ var id = 1; return { getId: function(){ return id; }, setId: function(_id){ id = _id; } } } var myObject = ObjId(); console.log(myObject.getId()); // => 1 myObject.setId(10); console.log(myObject.getId()); // => 10
Hàm objId trả về một đối tượng bao gồm 2 hàm closures là getId và setId. Các hàm closures này sử dụng chung một biến cục bộ là id.
Ban đầu, mình gọi myObject.getId() thì kết quả trả về là 1 (giá trị của biến cục bộ). Sau đó, mình gọi myObject.setId(10). Nếu hàm closures chỉ lưu biến cục bộ theo giá trị thì nghĩa là giá trị của biến cục bộ id sẽ không thay đổi.
Nhưng khi mình gọi tiếp myObject.getId() thì giá trị trả về là 10. Chứng tỏ, hàm closures phải lưu biến cục bộ theo kiểu tham chiếu.
Bây giờ đọc lại ví dụ trên một lần nữa, bạn có thấy nó giống với cách sử dụng class (lớp) trong C++ hay Java không? Mình thì thấy có giống. Thực ra đây là một trong những cách tạo đối tượng (object) trong JavaScript.
So sánh hiệu năng giữa việc sử dụng closures và prototype
Ví dụ trên minh hoạ cách sử dụng closures. Bây giờ mình sẽ viết lại nó theo cách sử dụng prototype:
function ObjId(){ this.id = 1; } ObjId.prototype.getId = function(){ return this.id; }; ObjId.prototype.setId = function(_id){ this.id = _id; }; var myObject = new ObjId(); console.log(myObject.getId()); // => 1 myObject.setId(10); console.log(myObject.getId()); // => 10
Kết quả trả về vẫn giống y hệt như trên. Tuy nhiên, cách nào sẽ chạy nhanh hơn? Closures hay Prototype?
Dựa theo kết quả so sánh tại bài viết Performance of prototype vs closure code in JavaScript, mình tóm tắt lại như sau:
- Định nghĩa đối tượng: JavaScript closures nhanh hơn.
- Khả năng tiết kiệm bộ nhớ và khởi tạo đối tượng mới: Prototype nhanh hơn.
- Truy cập hàm (getter, setter): Prototype nhanh hơn.
Một cách khách quan, việc sử dụng prototype sẽ nhanh hơn và tiết kiệm bộ nhớ hơn việc sử dụng closures. Tuy nhiên, mình vẫn không phủ nhận vai trò của closures trong việc giúp bạn viết code ngắn gọn và rõ ràng hơn. Và dù sao, việc sử dụng closures là không bắt buộc. Tất cả tuỳ thuộc vào bạn. Xem thêm JavaScript prototype là gì?
Kết luận
Bài viết trên là khá ngắn gọn. Hy vọng nó phần nào giúp bạn hiểu được JavaScript closures là gì và cách sử dụng closures.
Tham khảo:
- Closures
- Understand JavaScript Closures With Ease
Có thể bạn quan tâm:
- JavaScript – The Core – Object & Prototype
- Scope và Closure trong Javascript
- Arguments object trong javascript là gì? Cách sử dụng?
Xem thêm việc làm JavaScript Developer hot nhất tại TopDev
TopDev via CompleteJavaScript.com