-
Notifications
You must be signed in to change notification settings - Fork 1
JavaScript Closures
Như đã nói trong bài tìm hiểu function JavaScipt, bài viết này tôi sẽ giới thiệu với các bạn về JavaScript closures. 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 closures là gì và cách sử dụng nó như thế nào.
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 tôi 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ụ:
var YEAR = '2017';
function greet(name) {
var intro = 'Hello ';
function sayHello(){
var hello = intro + name + ' in ' + YEAR;
console.log(hello);
}
sayHello();
}
greet('Lam Pham');
// => Hello Lam Pham in 2017
Ở đây, hàm sayHello là một hàm closures. Hàm này có thể sử dụng biến số ở toàn cục là YEAR, biến ở hàm chứa nó là intro và hello.
Nếu bạn muốn hiểu sâu và vận dụng closures thì sau đây là những đặc điểm quan trọng mà bạn cần nắm vững.
Thông thường, khi một hàm số đã return thì biến cục bộ trong nó cũng sẽ được giải phóng. Nhưng với closures, bạn vẫn có thể truy cập đến những biến cục bộ đó ngay cả khi outer function đã thực hiện xong.
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 tôi 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.
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, tôi gọi myObject.getId() thì kết quả trả về là 1 (giá trị của biến cục bộ). Sau đó, tôi 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 tôi 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? Tôi 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 mà tôi sẽ giới thiệu trong những bài viết tiếp theo.
Ví dụ trên minh hoạ cách sử dụng closures. Bây giờ tôi 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, tôi 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, tôi 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.
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.
- Closures
- Understand JavaScript Closures With Ease
- Performance of prototype vs closure code in JavaScript
Bản gốc: Blog Complete JavaScript.
Bài viết cùng chuyên mục:
- Giới thiệu tổng quan về ngôn ngữ lập trình JavaScript
- Values, types và operators trong JavaScript – Phần 1
- Values, types và operators trong JavaScript – Phần 2
- Cấu trúc chương trình trong JavaScript
- Tìm hiểu function JavaScript
- JavaScript Array cơ bản
- Array Sorting – vấn đề muôn thuở
- JavaScript Object – last but not least
- JavaScript forEach là cái quái gì?
- Phân biệt call, apply và bind trong JavaScript
- Tìm hiểu về JSON trong JavaScript
- Lập trình hướng đối tượng với JavaScript?
- Các khía cạnh lập trình hướng đối tượng trong JavaScript
- Debug JavaScript – dễ hay khó?
- Một số lỗi khi sử dụng Strict mode JavaScript
- Tìm hiểu Regular Expression JavaScript
- Cơ bản về DOM JavaScript
- Xử lý một số Event JavaScript cơ bản
- XMLHttpRequest – Tạo HTTP request đến server trong JavaScript