Closure trong Javascript
Như bạn đã biết, mỗi một biến trong javascritp được định nghĩa với một phạm vi nhất định. Nếu bạn định nghĩa ở phía ngoài một hàm thì biến này có phạm vi global , ngược lại nếu nó được định nghĩa trong một hàm thì nó có phạm vi local đối với hàm đó, nghĩa là biến này chỉ có thể được truy cập bên ...
Như bạn đã biết, mỗi một biến trong javascritp được định nghĩa với một phạm vi nhất định. Nếu bạn định nghĩa ở phía ngoài một hàm thì biến này có phạm vi global, ngược lại nếu nó được định nghĩa trong một hàm thì nó có phạm vi local đối với hàm đó, nghĩa là biến này chỉ có thể được truy cập bên trong hàm. Như ở ví dụ dưới, có 2 biến sitename được định nghĩa khác nhau một ở phạm vi global với giá trị là undefined một ở trong hàm có giá trị là Hoclaptrinh.org:
var sitename; function a() { var sitename = 'Hoclaptrinh.Org'; alert(sitename); } alert(sitename); // undefined a(); // Hoclaptrinh.Org
Lexical Scope
Lexical scope là khái niệm trong Javascript chỉ ra rằng một hàm khi được khai báo sẽ định nghĩa ra phạm vi của riêng nó chứ không phải là lúc hàm này được gọi. Hãy xem ví dụ sau:
function f2() { alert(a); } function f1() var a = 1; f2(); } // call f1 f1(); // undefined
Hàm f1 khi được gọi sẽ tạo ra một cửa sổ alert với giá trị là undefined thay vì là 1 vì mặc dù a được định nghĩa trong hàm này nó lại không được định nghĩa trong hàm h2 (nơi mà hàm alert được gọi). Ở đây, hàm f2khi được khai báo nó không định nghĩa bất cứ biến a nào, vì vậy khi được gọi, giá trị hiển thị trong hộp cảnh báo sẽ là undefined thay vì 1.
Sử Dụng Closure
Từ ví dụ trên, bạn có thể thấy rằng lexical scope trong javascript quy định các phạm vi khác nhau cho mỗi hàm. Và biến được khai báo trong một hàm không thể truy cập được bởi hàm khác. Tuy nhiên sử dụng Closure cho phép bạn làm được điều này như ở trong hình minh họa dưới đây, biến b trong hàm B có thể được truy cập bên trong hàm C mặc dù hàm này có phạm vi khác với hàm B:
Xem ví dụ sau:
function B () { var b = "b"; return function C () { alert(b); } } var n = B(); n();
Với ví dụ này khi gọi hàm n()hộp thoại cảnh báo sẽ hiển thị giá trị của b là b. Ở đây, hàm C được định nghĩa bên trong hàm Bđược gọi là closure và hàm này có thể truy cập mọi biến được định nghĩa trong phạm vi của hàm B.
Sự khác biệt khi sử dụng closure đó là hàm C được định khai báo nằm bên trong hàm B và chính vì vậy giá trị của các biến trong hàm B vẫn có thể được truy cập bởi C.
Ví dụ thứ 2 thú vị hơn sau đây sẽ thể minh họa sinh động hơn về closure:
var n; function f () { var b = "b"; n = function() { alert(b); } } f();
Điều gì sẽ xảy ra khi bạn gọi hàm n()? Lúc này giá trị của biến b sẽ được hiển thị vì n là hàm closure được khai báo bên trong fvà biến bcũng được khai báo bên trong f. Như vậy n có thể truy cập giá trị của b.
Điều thú vị ở đây là rõ ràng n là biến được khai báo với phạm vi global nhưng cuối cùng lại có thể truy cập được biến b có phạm vi bên trong hàm f!
Cuối cùng để kết thúc bài viết, bạn đọc có thể tham khảo một bài tập sau:
var a = 200; function test () { alert(a); var a = 100; }
Điều gì xảy ra khi hàm test() được gọi?