JS閉包的運用及匿名函式的作用
轉載:https://blog.csdn.net/a250758092/article/details/52638209
配合上一篇js函式分類的部落格
1:閉包的目的
閉包的目的就是為了變數私有制,如果學過其他語言可以聯想到protected 這個關鍵詞,就是防止其他物件直接訪問私有的屬性或成員方法/函式,而只能通過呼叫 含有私有變數的物件來處理這個變數。
2:閉包的問題根源
有時候,我們希望有一個全域性變數,能夠作為唯一標識去讀取資料,但是全域性變數都屬於window物件,這就產生了一個問題,那就是這個變數能在任何情況下被呼叫,修改,導致穩健性不夠高,維護方面也會產生很大的憂患。舉個典型的例子:計時器;
var counter = 0;
function add() {
counter += 1;
}
add();
add();
add();
counter作為計數器,每當我們呼叫add的時候我們都能正常計數,但是即使不呼叫add,而是直接counter+=1;計數器一樣能夠增加,這並不是我們想要的。
那麼我們為何不把計數器寫在函式內部呢?
function add() {
var counter = 0;
counter += 1;
}
add();
add();
add();
此時我們發現,每次呼叫add函式,計數器都被重置了,這跟我們想要不同,那麼如何處理這個問題,讓計數器即可以作為“全域性變數”一樣去計數,又不至於直接通過增加計數器這個變數都能修改計數值?(其實它是區域性變數而不是全域性變數,只是因為它沒有被釋放而一直存在,而達到類似全域性變數的效果而已,其本質就是變數私有制!)
3:內嵌函式
所有函式都能訪問全域性變數。
實際上,在 JavaScript 中,所有函式都能訪問它們上一層的作用域。
JavaScript 支援巢狀函式。巢狀函式可以訪問上一層的函式變數。
該例項中,內嵌函式 plus() 可以訪問父函式的 counter 變數:
function add() {
var counter = 0;
function plus() {counter += 1;}
plus();
return counter;
}
如果我們能在外部訪問 plus() 函式,這樣就能解決計數器的困境。
但是我們同樣需要確保 counter = 0 只執行一次。
解決方式就是閉包。
4:閉包寫法
var add=(function(){
var counter=0;
return function(){//閉包
counter+=1;
}
})();
add();
add();//輸出結果為2
解釋:add綁定了一個匿名函式(匿名函式防止變數汙染,同時也可以防止其他函式直接呼叫),這個匿名函式返回值是一個閉包,也就相當於add綁定了一個閉包,每次呼叫add函式,計數器counter都會+1,而因為子函式(閉包)的存在,導致父函式(建立counter變數的函式)也一直存在。counter的生命週期也就一直存在,其效果就是“全域性變數”的效果,但是由於匿名函式的原因,我們無法通過直接訪問counter實現增加計數器,因為counter是匿名函式的區域性變數。然而這個區域性變數卻因為閉包的原因一直存在。這就是閉包的原理。
關於(function(){})();這種寫法,意思是把函式作為表示式,加上後面的(),表示立即執行,要知道小括號的作用就是把括號裡面的程式碼塊分組並返回值,而包裹匿名函式,返回的就是一個Function物件,再加上一個(),就跟平常呼叫函式的形式一樣。正如我們在看JQ外掛的時候,其最基本的程式碼格式就是(function(){})()
;
5:變數汙染
當存在多個JS檔案的時候,特別是專案檔案數量很龐大的時候,變數汙染的機率會更高,所謂變數汙染,就像a.js b.js兩個檔案都存在一個叫
clear的全域性變數,那麼window.clear既可以是a.js的變數,也可以是b.js的變數,其值甚至取決於你匯入檔案的順序,一般這個變數會是最後匯入的那個。
匿名函式(function(){})();把這些全域性變數變成了區域性變數,雖然防止了變數汙染,但是又出現了另外一個問題,那就是“通訊”問題。a.js b.js之間的變數無法相互訪問,我們不可能又建立一個全域性變數來通訊,這就回到了起點。為了恢復通訊而又能儘可能阻止變數汙染,我們可以使用唯一的全域性變數
var GLOBAL{}
通過自定義名稱空間來區分,這些變數到底是哪些檔案的
比如a.js 中 GLOBAL.A.a
b.js 中 GLOBAL.B.a
這樣就可以區分開了,所以名稱空間的書寫規範,必須得有自己的一套標準。