Tóm lại, Javascript Closures cho phép một hàm truy cập vào một biến được khai báo trong hàm cha mẹ từ vựng .
Chúng ta hãy xem một lời giải thích chi tiết hơn. Để hiểu các bao đóng, điều quan trọng là phải hiểu cách JavaScript phạm vi các biến.
Phạm vi
Trong phạm vi JavaScript được xác định với các chức năng. Mỗi chức năng xác định một phạm vi mới.
Hãy xem xét ví dụ sau đây;
function f()
{//begin of scope f
var foo='hello'; //foo is declared in scope f
for(var i=0;i<2;i++){//i is declared in scope f
//the for loop is not a function, therefore we are still in scope f
var bar = 'Am I accessible?';//bar is declared in scope f
console.log(foo);
}
console.log(i);
console.log(bar);
}//end of scope f
gọi cho bản in
hello
hello
2
Am I Accessible?
Bây giờ chúng ta hãy xem xét trường hợp chúng ta có một hàm g
được định nghĩa trong một hàm khác f
.
function f()
{//begin of scope f
function g()
{//being of scope g
/*...*/
}//end of scope g
/*...*/
}//end of scope f
Chúng tôi sẽ gọi f
các mẹ từ vựng của g
. Như đã giải thích trước đây chúng ta có 2 phạm vi; phạm vi f
và phạm vig
.
Nhưng một phạm vi là "trong" phạm vi khác, vậy phạm vi của hàm chức năng con là một phần của phạm vi của hàm cha? Điều gì xảy ra với các biến được khai báo trong phạm vi của hàm cha; Tôi có thể truy cập chúng từ phạm vi của hàm con không? Đó chính xác là nơi đóng cửa bước vào.
Đóng cửa
Trong JavaScript, hàm g
không chỉ có thể truy cập bất kỳ biến nào được khai báo trong phạm vi g
mà còn truy cập vào bất kỳ biến nào được khai báo trong phạm vi của hàm cha f
.
Cân nhắc làm theo;
function f()//lexical parent function
{//begin of scope f
var foo='hello'; //foo declared in scope f
function g()
{//being of scope g
var bar='bla'; //bar declared in scope g
console.log(foo);
}//end of scope g
g();
console.log(bar);
}//end of scope f
gọi cho bản in
hello
undefined
Hãy nhìn vào dòng console.log(foo);
. Tại thời điểm này, chúng tôi đang ở trong phạm vi g
và chúng tôi cố gắng truy cập vào biến foo
được khai báo trong phạm vi f
. Nhưng như đã nêu trước khi chúng ta có thể truy cập bất kỳ biến nào được khai báo trong hàm cha từ vựng, đó là trường hợp ở đây; g
là cha mẹ từ vựng của f
. Do đó hello
được in.
Bây giờ chúng ta hãy nhìn vào dòng console.log(bar);
. Tại thời điểm này, chúng tôi đang ở trong phạm vi f
và chúng tôi cố gắng truy cập vào biến bar
được khai báo trong phạm vi g
. bar
không được khai báo trong phạm vi hiện tại và hàm g
không phải là cha của f
, do đó bar
không được xác định
Trên thực tế, chúng ta cũng có thể truy cập các biến được khai báo trong phạm vi của hàm "grand Parent" từ vựng. Do đó, nếu có một hàm h
được định nghĩa trong hàmg
function f()
{//begin of scope f
function g()
{//being of scope g
function h()
{//being of scope h
/*...*/
}//end of scope h
/*...*/
}//end of scope g
/*...*/
}//end of scope f
sau đó h
sẽ có thể truy cập vào tất cả các biến được khai báo trong phạm vi chức năng h
, g
và f
. Điều này được thực hiện với việc đóng cửa . Trong các bao đóng JavaScript cho phép chúng ta truy cập bất kỳ biến nào được khai báo trong hàm cha từ vựng, trong hàm cha từ vựng lớn, trong hàm cha lớn từ vựng, v.v. Điều này có thể được xem như là một chuỗi phạm vi ; scope of current function -> scope of lexical parent function -> scope of lexical grand parent function -> ...
cho đến khi hàm cha cuối cùng không có cha mẹ từ vựng.
Đối tượng cửa sổ
Trên thực tế, chuỗi không dừng ở chức năng cha mẹ cuối cùng. Có một phạm vi đặc biệt hơn; các phạm vi toàn cầu . Mỗi biến không được khai báo trong hàm được coi là khai báo trong phạm vi toàn cục. Phạm vi toàn cầu có hai chuyên ngành;
- mọi biến được khai báo trong phạm vi toàn cầu đều có thể truy cập ở mọi nơi
- các biến được khai báo trong phạm vi toàn cục tương ứng với các thuộc tính của
window
đối tượng.
Do đó, có chính xác hai cách khai báo một biến foo
trong phạm vi toàn cầu; hoặc bằng cách không khai báo nó trong một hàm hoặc bằng cách đặt thuộc tính foo
của đối tượng cửa sổ.
Cả hai lần thử đều sử dụng số lần đóng
Bây giờ bạn đã đọc một lời giải thích chi tiết hơn, bây giờ có thể rõ ràng rằng cả hai giải pháp đều sử dụng các bao đóng. Nhưng để chắc chắn, hãy làm một bằng chứng.
Hãy tạo một ngôn ngữ lập trình mới; JavaScript-Không đóng cửa. Như tên cho thấy, JavaScript-No-Clos giống hệt với JavaScript trừ khi nó không hỗ trợ Đóng.
Nói cách khác;
var foo = 'hello';
function f(){console.log(foo)};
f();
//JavaScript-No-Closure prints undefined
//JavaSript prints hello
Được rồi, hãy xem điều gì xảy ra với giải pháp đầu tiên với JavaScript-Không đóng cửa;
for(var i = 0; i < 10; i++) {
(function(){
var i2 = i;
setTimeout(function(){
console.log(i2); //i2 is undefined in JavaScript-No-Closure
}, 1000)
})();
}
do đó, điều này sẽ in undefined
10 lần trong JavaScript-Không đóng.
Do đó giải pháp đầu tiên sử dụng đóng cửa.
Hãy xem xét giải pháp thứ hai;
for(var i = 0; i < 10; i++) {
setTimeout((function(i2){
return function() {
console.log(i2); //i2 is undefined in JavaScript-No-Closure
}
})(i), 1000);
}
do đó, điều này sẽ in undefined
10 lần trong JavaScript-Không đóng.
Cả hai giải pháp sử dụng đóng cửa.
Chỉnh sửa: Giả định rằng 3 đoạn mã này không được xác định trong phạm vi toàn cầu. Mặt khác, các biến foo
và i
sẽ được liên kết với window
đối tượng và do đó có thể truy cập thông qua window
đối tượng trong cả JavaScript và JavaScript-Không đóng.