Closure là gì? Kiến thức tổng quan Developer nên nắm vững 

ITNavi 02 Dec 2020 9782
Closures là một trong những khái niệm quan trọng được sử dụng vô cùng phổ biến trong giới công nghệ thông tin. Closures là một trong những yếu tố quan trọng quyết định xem bạn có được được tăng lương hay không. Ngoài ra, những Developer hiểu rõ về Closure sẽ giúp bạn tăng nhanh cấp bậc trong doanh nghiệp. Chính vì vậy, hãy cùng ITNavi dành 1 chút thời gian để tìm hiểu Closure là gì nhé! 

Định nghĩa Closure là gì?

Closure được định nghĩa là một trong những hàm được viết lồng vào trong hàm khác. Nó được sử dụng để biến đổi toàn cục, biến cục bộ của chính nó.  Closure có một thuộc tính cực mạnh của Javascript và hầu hết các loại ngôn ngữ lập trình khác.  Bạn có thể tham khảo định nghĩa JavaScript Closures là gì như sau:  JavaScript Closures là một dạng tập hợp bao gồm môi trường nơi hàm số đã được khai báo và một hàm. Tại đây, môi trường sẽ bao gồm những biến cục bộ trong phạm vi hàm số đã được khai báo.  Định nghĩa Closure là gì?

Định nghĩa Closure là gì?

Hàm closures có khả năng truy cập biến số ở 3 phạm vi khác nhau như sau: 
  • Biến dạng toàn cục. 
  • Biến đã được khai báo ở hàm số có chứa sẵn hàm closures (outer function)
  • Biến nằm ở trên hoặc trong hàm closures

Closure được trông như thế nào nhỉ? 

Phía trên là cách viết phổ biến và bạn chỉ cần viết lại như sau: 
closure function init() {    var name = "Closure";    function hi() {        console.log('my name is ' + name);    }    return hi; // lưu ý không viết là return hi() } var f = init(); f();
  Function nằm trong JS cũng là một trong những loại data type, tuy nhiên loại data type thường đặc biệt hơn so với loại khác như string, number,... ở điểm là nó có thể chạy được (executable). Bởi vì, function cũng là một trong những loại data nên nó cũng có thể được dùng để làm các giá trị được trả về ở dạng câu lệnh return. Tất cả các object nằm trong JS thường có type là function thì sẽ đều có thể chạy được bằng cách thêm cặp() và đằng sau nó thì cách người ta gọi là f(). Đến đây, thì chắc hẳn người dùng có thể sẽ phải đặt ra câu hỏi là: tại sao nó lại không xảy ra lỗi.  Đáp án như sau: Nếu trong Java hoặc C, C++, nếu như một dạng hàm return thì các biến local của nó có thể bị thu hồi để dành tài nguyên cho bộ nhớ với các thao tác khác. Vậy thì, tại sao gọi hàm f() mà vẫn in ra được name là Closure trong khi đáng lẽ biến name phải được thu hồi? Đây chính là một trong những điểm đặc biệt chỉ có ở Closure, khi bạn thực hiện return hàm hi bên trong hàm init thì thực chất cả môi trường này nơi hi được tạo ra đều sẽ được gắn với hi dưới dạng một reference và mỗi dạng đều tương tự như pointer trong C++. Từ đó, kết quả được cho ra khi ta gọi hàm f() thì name vẫn còn được tồn tại bởi nó được lấy ra từ ENV đi kèm theo đó. Nếu như trên tab console của Chrome Developer Tools thì bạn tiếp tục gõ console.dir(f) xong thì có thể thấy rõ:  Kèm theo f sẽ là một mảng Scopes sở hữu 2 item Closure và Global. Chính vì vậy, có thể nói Closure chính là local scope nói ở trên và hiện đang có một giá trị name là: "Closure". Closure được sử dụng để tái hiện lại bản hack

Closure được sử dụng để tái hiện lại bản hack

Chính vì vậy, lý do mà bạn cần phải gắn kèm với reference của ENV dựa theo closure là khả năng chạy được bất đồng bộ của JS. Ngay sau khi return hi hàm init đã được hoàn tất, từ đó các biến local đều sẽ bị thu hồi và trong số đó có name. Để đảm bảo cho hi có thể chạy được thì điều cần thiết cũng là cách đơn giản nhất bạn cần làm là chụp lại ảnh ENV – trong số đó cần phải chứa name-  từ đó gửi kèm theo hi ở dưới dạng một reference. Ở đây, reference là một trong cách cách giúp giải quyết hiệu quả về năng suất của bộ nhớ (nếu như bạn từng dùng Prototype thì nó khá quen thuộc). Bạn đọc tham khảo thêm: Tìm hiểu NPM là gì? Công dụng và cách thức hoạt động của NPM

Tìm hiểu về hàm closures lưu trữ biến số của outer function theo dạng tham chiếu

Hàm objId được trả về với một đối tượng đã được bao gồm 2 hàm closures là: getId và setId. Những hàm closures này đều được dùng chung một loại biến cục bộ là id.  Đầu tiên: bạn có thể gọi myObject.getId() thì kết quả được trả về là một giá trị dạng biến cục bộ. Sau đó, nếu gọi myObject.setId(10), nếu như hàm closures chỉ lưu trữ biến cục bộ theo giá trị thì giá trị cục bộ của biến cục bộ id sẽ giữ nguyên.  Sau đó, gọi myObject.getId() thì giá trị được trả về là 10. Chứng tỏ là hàm closures cần phải lưu biến cục bộ dựa theo kiểu tham chiếu.  Đây chính là một trong những cách tạo đối tượng ở trong JavaScript.

Các ứng dụng thực tế của Closure là gì?

Để nắm rõ được ứng dụng của Closure là gì thì trước tiên bạn hãy cùng ITNavi xem xét một số ví dụ thực tế của closure như sau: Ví dụ sẽ bao gồm 1 trường hợp có cùng function nhưng khác ENV và đều có cùng ENV nhưng khác function. Closure là 1 trong những yếu tố quan trọng với lập trình viên

Closure là 1 trong những yếu tố quan trọng với lập trình viên

Trường hợp 1: Function factory Ví dụ như sau:
function makeExponentiation(x) {    var exponent = x;    return function(y) {        return Math.pow(y, exponent);    } } var sqr = makeExponentiation(2); var sqrt = makeExponentiation(0.5); console.log('3 bình phương là ' + sqr(3)); console.log('căn bậc hai của 9 là ' + sqrt(9));
  Với ví dụ trên thì bạn có thể thấy rằng, hàm makeExponentiation trông giống như một function factory và nó có thể tạo ra được các function khác tùy vào tham số được truyền vào. Trong đó, sqr và sqrt đều là 2 closure sở hữu body giống nhau nhưng ENV đều khác nhau. Nếu như, ENV của sqr có chứa {exponent: 2} thì của sqrt sẽ chứa {exponent: 0.5}. ENV của mỗi một closure thì chỉ chứa các biến hay hàm mà nó đã được sử dụng, ở đây thì nó được xem là biến exponent. Với việc, ENV có chứa tất cả những biến local và outer lại rất thừa thãi cũng như không mang lại hiệu quả về mặt hiệu năng.  Trường hợp 2: Mô phỏng lại phạm vi của biến trong OOP (variable visibility) Bên trong JS sẽ không có bất kỳ khái niệm Class nào được sử dụng đúng nghĩa như trong C++. Từ đó, khái niệm Class trong ES6 chỉ là cách khai báo và một cách hack. Sử dụng closure mô phỏng lại cách hack này như sau: 
function Counter() {    var counter = 0;    function add(number) {        counter += number;    }    return {        increment: function() {            add(1);        },        decrement: function() {            add(-1);        },        value: function() {            return counter;        }    };   }); var counter = Counter(); console.log('giá trị ban đầu ' + counter.value()); counter.increment(); counter.increment(); console.log('sau khi tăng 2 lần ' + counter.value()); counter.decrement(); console.log('sau khi giảm 1 lần ' + counter.value());
  Mỗi một hàm increment, decrement và value chính là closure có body khác nhau nhưng nó lại chia sẻ chung một ENV. Khi chia sẻ chung ENV là bí quyết mô phỏng Class trong JS. Khi closure update một biến thì việc thay đổi này cũng sẽ được ghi nhận tại các closure khác. Các closure này đều đã được thao tác trên một tập biến tương tự nhau. Ngoài class Counter thì ta không còn cách nào để có thể truy xuất trực tiếp được các biến counter của nó. Tuy nhiên, bạn có thể thay đổi counter thông qua những hàm public increment, decrement. Behavior này đã có thể mô phỏng một cách gần đúng với một Class trong ngôn ngữ lập trình khác như PHP hoặc Java.

Tổng kết

Chắc rằng, với những chia sẻ từ ITNavi ở phía trên thì bạn đọc đã phần nào hiểu được Closure là gì rồi đúng không nào? Hiện nay, Closure là một trong những yếu tố quan trọng quyết định đến khả năng thăng tiến của lập trình viên trong tương lai. Chính vì vậy, mỗi một Developer nên trang bị cho bản thân những thông tin cần thiết nhất về nó để nâng cao khả năng thăng tiến của mình. Ngoài ra, có thể thấy rằng kiến thức về Closure không quá nhiều nên nếu bạn cần đẩy mạnh kiến thức trong mảng này thì đừng quên trau dồi kiến thức thường xuyên nhé!
ITNavi - Nền tảng kết nối việc làm IT

Nguồn: Closure là gì? Kiến thức tổng quan Developer nên nắm vững 

Bài viết liên quan

NEWSLETTER

Nhập địa chỉ email của bạn dưới đây để đăng ký nhận tin mới nhất

KẾT NỐI VÀ THEO DÕI