TÌM HIỂU 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.

Khái niệm JavaScript closures

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à introhello.

Một số đặc điểm quan trọng của JavaScript Closures

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.

Hàm closures có thể truy cập tới biến số của hàm chứa nó, dù cho hàm đó đã return

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 functionintrolocal.

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.

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à getIdsetId. 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.

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ờ 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.

Tham khảo

Bản gốc: Blog Complete JavaScript.
Facebook Fanpage: Complete JavaScript
Facebook Group: Hỏi đáp JavaScript VN

Bình luận


White
{{ comment.user.name }}
Bỏ hay Hay
{{comment.like_count}}
Male avatar
{{ comment_error }}
Hủy
   

Hiển thị thử

Chỉnh sửa

White

Lam Pham

23 bài viết.
40 người follow
Kipalog
{{userFollowed ? 'Following' : 'Follow'}}
Cùng một tác giả
White
24 10
Trước khi vào nội dung bài viết. Tôi xin đính chính giúp anh trả lời những câu hỏi chỉ là tựa đề của một bài hát tôi không có ý xưng hô là anh. Bài...
Lam Pham viết 6 tháng trước
24 10
White
13 6
Có thể bạn thừa biết, JavaScript là một ngôn ngữ chạy (Link). Điều đó có nghĩa là nếu bạn thực hiện một tác vụ quá lớn trên giao diện chính thì khả...
Lam Pham viết 6 tháng trước
13 6
White
13 2
Xin chào bạn Có thể bạn đã biết, JavaScript là một ngôn ngữ lập trình rất mạnh, nhưng lại vô cùng rắc rối, phức tạp. Để có thể nắm vững được Java...
Lam Pham viết 2 tháng trước
13 2
Bài viết liên quan
White
6 0
Có bao giờ bạn thắc mắc, chuyện gì thực sự diễn ra khi chúng ta gõ một địa chỉ trang web (ví dụ: (Link)) lên trình duyệt và nhấn Enter? Đầu tiên, t...
Lam Pham viết 6 tháng trước
6 0
White
2 4
Từ xưa đến nay, sắp xếp giữ một vai trò vô cùng quan trọng. Nhiều ứng dụng (từ điển, danh bạ, quản lý tài khoản,…) thường có chức năng sắp xếp theo...
Lam Pham viết 6 tháng trước
2 4
{{like_count}}

kipalog

{{ comment_count }}

bình luận

{{liked ? "Đã kipalog" : "Kipalog"}}


White
{{userFollowed ? 'Following' : 'Follow'}}
23 bài viết.
40 người follow

 Đầu mục bài viết

Vẫn còn nữa! x

Kipalog vẫn còn rất nhiều bài viết hay và chủ đề thú vị chờ bạn khám phá!