JavaScript 閉包那些事
在計算機中,閉包指引用了自由變數的函式。這個被引用的自由變數將和這個函式一同存在。
侷限自定義: 在Javascript 中子函式使用了其父函式或者外層函式的變數就產生了一個閉包。這時外層變數的值能被子函式使用且外層變數在子函式未銷燬之前一直被分配不會被釋放。
說源頭說起:
閉包的產生由於變數作用域鏈引起的(由詞法作用域 導致)。
從JavaScript作用域說閉包:
在ES5及之前的語言規範中作用域分3種:
- 全域性作用域
- 區域性(函式作用域)
- eval作用域。 [注意:沒有 塊級作用域]
在函式中定義的變數,就屬於區域性作用域,且只對函式範圍內其他表示式可見。 而函式內部又可以使用父函式中的變數這就是由於作用域鏈,當JavaScript查詢與變數關聯的值時,會遵循一個查詢鏈。這個鏈是基於作用域的層次結構。 如下程式碼:
var a = "global variable"; ( function () { console.log(a); var fn = function () { var a = "local variable" console.log(a); } fn(); })() //輸出 //global variable //local variable 複製程式碼
我們在window
全域性物件下聲明瞭變數a
,隨後呼叫了一個立即執行函式,其中向控制檯直接列印變數a,由於立即執行函式沒有宣告區域性變數a所以導致JavaScript向其作用域鏈繼續查詢接著就在window
物件中找到a
變數並打印出它的值"global variable"。接著這個立即執行函式聲明瞭一個區域性函式變數再呼叫它,在這個函式變數中首先聲明瞭一個區域性變數a
然後在向控制檯輸出a
得值。JavaScript在執行時由於在fn函式作用域內部查詢到了變數a
就直接使用變數a
的值所以打印出了local variable。
值得注意的是:
window
清楚作用域的含義了嗎???
那麼我們剛剛說的“詞法 “作用域又是什麼。 其實詞法就是指代環境:由於函式決定作用域,並且函式是一等公民可以直接用來引數傳遞等。那麼作用域鏈是怎樣來確定的呢: 下面的話背熟了:作用域鏈是根據函式定義時候的位置確定的而不是在呼叫時。--這就是“詞法”作用域
如果你還不懂閉包我TM。。。
栗子:chestnut::
- [閉包的影響]對一些li繫結點選事件並列印其索引,對比2斷程式碼不解釋:
var liListlength = 3; for(var i=0;i<liListlength;i++){ var ele=document.querySelectorAll(".test > li")[i]; ele.addEventListener("click",function(){ alert("index is :" + i); }) } var liListlength = 3; for(var i=0;i<liListlength;i++){ var ele=document.querySelectorAll(".test > li")[i]; ele.addEventListener("click",(function(i){ return function(){ alert("index is :" + i); } })(i)) } 複製程式碼
- [閉包的妙用]模擬封裝 我們可以運用閉包模擬模組的實現,即我們可以只暴露方法介面隱藏區域性變數,具體如下:
var countMoudle = (function(){ var _count = 0; var plus = function(){ _count++; }; var minus = function(){ _count--; }; var print = function(){ console.log(_count); } return { plus: plus, minus: minus, print: print }; })(); countMoudle.print()//0 countMoudle.plus() countMoudle.print()//1 countMoudle.minus() countMoudle.print()//0 複製程式碼
此時我們只暴露出了方法名而沒有暴露出變數屬性,這時要對變數的修改只有通過介面方法呼叫。