Like most modern programming languages, JavaScript uses lexical scoping. This means that functions are executed using the variable scope that was in effect when they were defined, not the variable scope that is in effect when they are invoked. In order to implement lexical scoping, the internal state of a JavaScript function object must in- clude not only the code of the function but also a reference to the current scope chain. (Before reading the rest of this section, you may want to review the material on variable scope and the scope chain in §3.10 and §3.10.3.) This combination of a function object and a scope (a set of variable bindings) in which the function’s variables are resolved is called a closure in the computer science literature. (This is an old term that refers to the fact that the function’s variables have bindings in the scope chain and that therefore the function is 「closed over」 its variables.)javascript
Technically, all JavaScript functions are closures: they are objects, and they have a scope chain associated with them. Most functions are invoked using the same scope chain that was in effect when the function was defined, and it doesn’t really matter that there is a closure involved. Closures become interesting when they are invoked under a different scope chain than the one that was in effect when they were defined. This happens most commonly when a nested function object is returned from the function within which it was defined. There are a number of powerful programming techniques that involve this kind of nested function closures, and their use has become relatively common in JavaScript programming. Closures may seem confusing when you first en- counter them, but it is important that you understand them well enough to use them comfortably.html
JavaScript, The Definite Guidejava
翻譯成中文的話也許是這樣:編程
和大多數的現代化編程語言同樣,JavaScript
是採用詞法做用域的,這就意味着函數的執行依賴於函數定義的時候所產生(而不是函數調用的時候產生的)的變量做用域。爲了去實現這種詞法做用域,JavaScript
函數對象的內部狀態不只包含函數邏輯的代碼,除此以外還包含當前做用域鏈的引用。函數對象能夠經過這個做用域鏈相互關聯起來,如此,函數體內部的變量均可以保存在函數的做用域內,這在計算機的文獻中被稱之爲閉包。閉包
從技術的角度去將,全部的JavaScript
函數都是閉包:他們都是對象,他們都有一個關聯到他們的做用域鏈。絕大多數函數在調用的時候使用的做用域鏈和他們在定義的時候的做用域鏈是相同的,可是這並不影響閉包。當調用函數的時候閉包所指向的做用域鏈和定義函數時的做用域鏈不是同一個做用域鏈的時候,閉包become interesting。這種interesting的事情每每發生在這樣的狀況下: 當一個函數嵌套了另外的一個函數,外部的函數將內部嵌套的這個函數做爲對象返回。一大批強大的編程技術都利用了這類嵌套的函數閉包,固然,javascript
也是這樣。可能你第一次遇見閉包以爲比較難以理解,可是去明白閉包而後去很是自如的使用它是很是重要的。app
通俗點說,在程序語言範疇內的閉包是指函數把其的變量做用域也包含在這個函數的做用域內,造成一個所謂的「閉包」,這樣的話外部的函數就沒法去訪問內部變量。因此按照第二段所說的,嚴格意義上全部的函數都是閉包。編程語言
須要注意的是:咱們經常所說的閉包指的是讓外部函數訪問到內部的變量,也就是說,按照通常的作法,是使內部函數返回一個函數,而後操做其中的變量。這樣作的話一是能夠讀取函數內部的變量,二是可讓這些變量的值始終保存在內存中。ide
JavaScript
利用閉包的這個特性,就意味着當前的做用域老是可以訪問外部做用域中的變量。函數
function counter (start) { var count = start; return { add: function () { count ++; }, get: function () { return count; }, }; } var foo = counter (4); foo.add(); foo.get() //5
上面的代碼中,counter
函數返回的是兩個閉包(兩個內部嵌套的函數),這兩個函數維持着對他們外部的做用域counter
的引用,所以這兩個函數沒有理由不能夠訪問count
;ui
在JavaScript
中沒有辦法在外部訪問count
(JavaScript
不能夠強行對做用域進行引用或者賦值),惟一可使用的途徑就是以這種閉包的形式去訪問。
對於閉包的使用,最多見的多是下面的這個例子:
for (var i = 0; i < 10; i++) { setTimeout (function (i) { console.log (i); //10 10 10 .... }, 1000); }
在上面的例子中,當console.log
被調用的時候,匿名函數保持對外部變量的引用,這個時候for
循環早就已經運行結束,輸出的i
值也就一直是10
。但這在通常的意義上並非咱們想要的結果。
爲了得到咱們想要的結果,咱們通常是這樣作:
for (var i = 0; i < 10; i++) { (function (e) { setTimeout (function () { console.log (e); }, 1000); })(i); }
外部套着的這個函數不會像setTimeout
同樣延遲,而是直接當即執行,而且把i
做爲他的參數,這個時候e
就是對i
的一個拷貝。當時間達到後,傳給setTimeout
的時候,傳遞的是e
的引用。這個值是不會被循環所改變的。
除了上面的寫法以外,這樣的寫法顯然也是沒有任何問題的:
for (var i = 0; i < 10; i++) { setTimeout((function(e) { return function() { console.log (e); } })(i), 1000); }
或許,還能夠藉助這個故事理解一下:
I'm a big fan of analogy and metaphor when explaining difficult concepts, so let me try my hand with a story.
Once upon a time:
There was a princess...
function princess() {She lived in a wonderful world full of adventures. She met her Prince Charming, rode around her world on a unicorn, battled dragons, encountered talking animals, and many other fantastical things.
var adventures = []; function princeCharming() { /* ... */ } var unicorn = { /* ... */ }, dragons = [ /* ... */ ], squirrel = "Hello!";But she would always have to return back to her dull world of chores and grown-ups.
return {And she would often tell them of her latest amazing adventure as a princess.
story: function() { return adventures[adventures.length - 1]; } }; }But all they would see is a little girl...
var littleGirl = princess();...telling stories about magic and fantasy.
littleGirl.story();And even though the grown-ups knew of real princesses, they would never believe in the unicorns or dragons because they could never see them. The grown-ups said that they only existed inside the little girl's imagination.
But we know the real truth; that the little girl with the princess inside...
...is really a princess with a little girl inside.