js-----閉包淺談(Closure)
《開場白》
作為初級菜鳥的我,對與閉包知識,真的是一團迷霧,我這裡就先把我現階段對閉包的理解寫出來,先記一下,隨後對閉包理解深入了,再看看!
1.什麼是閉包?
“菜鳥教程”上這樣總結到:閉包就是一個函式引用另一個函式的變數,因為變數被引用著所以不會被回收,因此可以用來封裝一個私有變數。這是優點也是缺點,不必要的閉包只會增加記憶體消耗。或者說閉包就是子函式可以使用父函式的區域性變數,還有父函式的引數。看起來還是雲裡霧裡的,接著看看下面的例子吧!
2.回顧一下區域性變數和全域性變數
首先必須理解Javascript特殊的變數作用域。閉包這裡牽扯到變數,來吧
1. 全域性變數
2. 區域性變數:函式中用var定義的變數,只能在函式中訪問這個變數,函式外部訪問不了。
//函式內部可以直接讀取全域性變數。
var age = 18;
function f1(){
alert(age);
}
f1(); // --------------------18
//在函式外部自然無法讀取函式內的區域性變數。
function f2(){
var num=99;
}
f2();
alert(num); // ---num is not defined//無法讀取函式內的age
3.那麼如何從外部讀取區域性變數?
/*
可以利用函式內再宣告函式:內嵌函式
函式t2就被包括在函式t1內部,這時t1內部的所有區域性變數,對t2都是可見的。但是反過來就不行,t2內部的區域性變數,對t1就是不可見的。既然t2可以讀取t1中的區域性變數,那麼只要把t2作為返回值,我們不就可以在t1外部讀取它的內部變量了嗎!
*/
function t1(){
var age = 18;
function t2(){
alert(age);
}
return t2;
}
var tmp = t1();
tmp();//----------------------------18
大部分的語言,t1被呼叫執行,則申請記憶體,並把其區域性變數age, push入棧,t1函式執行完畢,內部的區域性變數,隨著函式的退出而銷燬.因此age = 20 的區域性變數已經消失了;
但是在js中,age = 20 這個變數,卻被t2捕捉,即使t1執行完畢,通過t2仍然可以訪問t2依然可以訪問該變數。這也是實現上面的訪問區域性變數。
再來看一下,這個情形的過程,引入js閉包的知識點。
在js中,t1執行過程中又生成了t2,而作用域上來說,t2能訪問到age = 20,於是age = 20 不會消失,而是與返回的t1函式形成了一個環境包,這個包是t2的(把其周圍的變數環境形成了封閉的環境包 共同返回),即使t1執行完畢,通過t2仍然可以訪問t1依然可以訪問該變數,這就是閉包!!!
4.小桃花穿越故事
再來舉個例子看懂閉包,通俗易懂的(燕十八老師講過的一個例子)
故事梗概:2017年12月3日,雙胞胎姐妹大桃花妹妹的小桃花因意外,穿越清朝 那麼他的姐姐是誰?
function closure(){
var sister = '大桃花';
var me = function(){
alert(sister);
}
return me;
}
function place(){
var sister = '清朝大福晉';
var girl = closure();
girl();
}
place();//------------------------------大桃花
5.閉包計數器
閉包來維護一個別人汙染不到的變數 做計數器
var fn = (function(){
var cnt = 0;
return function(){
return ++cnt;
}
})();
alert(fn());//------------------1
alert(fn());//------------------2
alert(fn());//------------------3
因為我們在上面的程式碼return回去了函式,然後因為這個自執行函式被fn引用所以裡面的變數cnt並沒有因為這個自執行函式執完而銷燬,而是儲存到了記憶體中,所以我們多次列印fn()就成了1、2、3
6.經典案例
閉包的使用場景
//沒有使用閉包,實現不了點哪一個li,就顯示數字幾
window.onload = function(){
var ul = document.getElementsByTagName("ul")[0];
var li = ul.getElementsByTagName("li");
for(var i=0;i<li.length;i++){
li[i].onclick = function(){
console.log(i); //不管我怎麼點都是返回都一樣
}
}
}
//用閉包來解決問題
window.onload = function(){
var ul = document.getElementsByTagName("ul")[0];
var li = ul.getElementsByTagName("li");
for(var i=1;i<=li.length;i++){
(function(i){
li[i].onclick = function(){
console.log(i); //點選第幾個返回第幾個
}
})(i)
}
}
閉包用途
本質上,閉包就是將函式內部和函式外部連線起來的一座橋樑。閉包可以用在許多地方。它的最大用處有兩個,一個是前面提到的可以讀取函式內部的變數,另一個就是讓這些變數的值始終保持在記憶體中。
《結束語》
聽說閉包用處很大(深入的理解JavaScript的閉包是邁向高階JS程式設計師的必經之路,理解了其解釋和執行機制才能寫出更為安全和優雅的程式碼),但是目前只掌握了這些,學習還是循序漸進的好,慢慢理解閉包吧!