Chào mừng bạn đến với Vimentor!

Hỏi đáp
Đăng ký

5. Closure nâng cao

Trong bài này, ta sẽ tìm hiểu về một tính năng hết sức quan trọng của closure, đó là có thể thay đổi giá trị biến được gắn kết khi chạy nền bên dưới.

Ta cùng nhắc lại về ví dụ ở bài cũ.

function dangky(phuongtien) {
          return function (loai) {
                   alert("Dang ky phuong tien giao thong " + phuongtien + " thuoc loai " + loai +".");
          }
}

Đây là một hàm đăng ký, nhận tham số đầu vào là phương tiện, và trả ra một hàm có tham số đầu vào là loại. Hàm này là anonymous function, và thực hiện alert thông báo trong đó có chứa cả hai tham số phương tiện và loại.

Giờ ta sẽ thực hiện nâng cấp hàm đăng ký trên.

function dangky(phuongtien) {
          var thutu = 0;
          return function (loai) {
                   thutu++;
                   alert("Dang ky phuong tien giao thong " + phuongtien + " thuoc loai " + loai +". Day la phuong tien duoc dang ky thu "+thutu);
          }
}

Biến thứ tự được khai báo thêm vào, giữ nhiệm vụ đếm số lần đăng ký phương tiện, và cho biết đây là phương tiện được đăng ký thứ bao nhiêu. Mỗi khi hàm được trả về, thứ tự sẽ được tăng thêm một đơn vị.

Ta sẽ thử gọi hàm như sau:

var xehoi = dangky("Xe 4 banh");

Lần đầu tiên, khi gọi biểu thức hàm xehoi:

xehoi("Innova");

Ta sẽ nhận được một popup, alert thông báo Đăng ký phương tiện giao thông Xe 4 bánh thuộc loại Innova. Đây là phương tiện được đăng ký thứ 1.

Lần thứ hai, khi gọi biểu thức hàm xehoi:

xehoi("Rolls Royce");

Ta sẽ nhận được một popup, alert thông báo Đăng ký phương tiện giao thông Xe 4 bánh thuộc loại Rolls Royce. Đây là phương tiện được đăng ký thứ 2.

Thật thú vị, đúng không nào? Rõ ràng, dù local scope của hàm chúng ta đã không còn nữa, giá trị của biến vẫn được giữ lại và tăng lên liên tục. Đây cũng chính là chỗ độc đáo của closure.

Tuy nhiên, việc sử dụng closure cũng có một số lưu ý nhất định, và trong một số trường hợp chúng ta phải cẩn thận khi sử dụng closure, nếu không kết quả trả về sẽ không như mong đợi.

Ta hãy xem ví dụ sau:

function timthutu(ten, danhsach) {
          var thongbao;
          for (var i = 0; i < danhsach.length; i++) {
                   if (danhsach[i] == ten) {
                             thongbao = function() {
                                      alert(ten + “ co thu tu “ + i + “ trong danh sach.”);
                             }
                   }
          }
          return thongbao;
}


var danhsach = ["Minh", "An", "Anh", "Ngoc"];
var tim = timthutu("An", danhsach);
tim();

Ta kỳ vọng đoạn mã trên sẽ alert ra popup là An có thứ tự 1 trong danh sách. Tuy nhiên kết quả thực tế sẽ ra popup là An có thứ tự 3 trong danh sách. Lý do là vì khi dùng closure, khi ta gọi đến biến có external scope, nó sẽ sử dụng tham chiếu (reference) chứ không phải tham trị (value). Do đó nó sẽ lấy giá trị sau cùng của biến i, chính là 3. Chúng ta phải hết sức cẩn thận khi sử dụng closure, nhất là trong những khi sử dụng vòng lặp.

Cách khắc phục đoạn code trên để có thể trả ra kết quả như mong đợi là như sau:

function timthutu(ten, danhsach) {
          for (var i = 0; i < danhsach.length; i++) {
                   if (danhsach[i] == ten) {
                             return function() {
                                      alert(ten + " co thu tu " + i + " trong danh sach.");
                             }
                   }
          }
}

Ta đã khắc phục bằng cách trả về thông báo ngay khi tìm được tên trong danh sách mà ta cần, và khi trả về ngay lập tức như thế, ta đã ngăn chận việc tiếp tục tăng giá trị của biến i trong vòng lặp.

Ở đây ta giới thiệu thêm một khái niệm gọi là lexical scoping, có nghĩa là các hàm Javascript được thực thi sử dụng chuỗi scope (scope chain) có hiệu lực theo thứ tự mà chúng được định nghĩa tuần tự.

 

Kết luận

Khi sử dụng closure, biến bên trong có thể giữ lại giá trị và tiếp tục thay đổi. Ngoài ra, việc sử dụng closure trong vòng lặp phải hết sức cẩn thận, vì khi ta gọi đến biến có external scope, nó sẽ sử dụng tham chiếu chứ không phải tham trị. Ở bài kế tiếp, chúng ta sẽ cùng tìm hiểu về kỹ thuật Hoisting.

Luyện tập cho bài học này

Khóa học liên quan

** Nếu bạn muốn viết các nội dung đặt biệt thì hãy làm theo hướng dẫn sau

Xem thêm 10 bình luận
Viết blog mới của bạn
Báo lỗi trang
Đang tải