今天看了關於js閉包方面的文章,還是有些雲裡霧裡,對於一個菜鳥來說,學習閉包確實有一定的難度,不說別的,能夠在網上找到一篇優秀的是那樣的不易。
當然之所以閉包難理解,個人覺得是基礎知識掌握的不牢,因為閉包牽扯到一些前面的東西,比如作用域\等等,如果連基本的作用域都沒有弄清楚,自然不可能搞懂閉包,還有就是對js的實踐比較少,因為你根本就不知道什麼時候要用這東西,自然談不上對閉包的深刻理解。
今天我就簡單的說說我目前所理解的閉包,當然可能不完全正確,但是我相信會給你一定的啟發。
首先我們來談談js中的變量,如果你不知道我為什麼要說這些,那麼你根本沒有掌握js的基礎,建議回頭復習。
js中分:全局變量 和 局部變量
全局變量:可以在任意位置訪問的量就叫全局變量
1 var age = 20; function a(){ console.log(age); >>20 } a();
局部變量:函數中用var定義的變量,只能在函數中訪問這個變量,函數外部訪問不了。
1 function a(){ var age = 20; } a(); console.log(age); >> Uncaught ReferenceError: age is not defined
注意點1:在函數中如果不使用var定義變量那麼js引擎會自動添加成全局變量。
注意點2:全局變量從創建的那一刻起就會一直保存在內存中,除非你關閉這個頁面,局部變量當函數運行完以後就會銷毀這個變量,假如有多次調用這個函數它下一次調用的時候又會重新創建那個變量,既運行完就銷毀,回到最初的狀態,簡單來說局部變量是一次性的,用完就扔,下次要我再重新創建。
函數的相關知識點:
1. 一個函數內可以嵌套多個函數
2. 函數裡面的子函數可以訪問它上級定義的變量,注意不只是一級,如果上級沒有會繼續往上級找,直到找到為止,如果找到全局變量到找不到就會報錯。
1 function a(){ var name = "何問起"; function b(){ console.log(name); >> "何問起" } b(); } a();
3. 函數的另外一種調用形式,你可以把它叫做自調用,自己調用自己,達到自執行的效果。
1 var a = 0; (function(){ console.log(++a); >>1 })()
這種方式用()把內容包裹起來,後面的()表示執行這個函數,可能你會問為什麼要把函數包起來,如果不包裹起來,js會把它當作函數聲明來處理,如果包裹起來就是表達式,還沒有看懂就上網查吧。
開始我們正式閉包部分---------------------------- 幣包 ---------------像錢包一樣的東西,可以把東西包裹起來----------
首先我們來看看為什麼需要學習閉包,加以理解 -- 0 v 0- -
1 function a(){ var num = 0; console.log(++num); } a(); >>1 a(); >>1
上面代碼輸出了兩次1,為什麼呢?如果你有看我上面的關於變量部分肯定能夠想到個大概。
前面我們說過了函數執行完以後,裡面的變量(即局部變量)就會銷毀,下一次運行又會重新創建那個變量,所以雖然你第一次++num了但是這個變量在第一次執行完畢以後就被銷毀了。
那麼我們怎麼樣才能確保第一次的變量不被銷毀,那麼就需要我們的閉包出場了。
溫馨提示:JavaScript中有回收機制,函數沒有被引用執行完以後這個函數的作用域就會被銷毀,如果一個函數被其他變量引用,這個函數的作用域將不會被銷毀,(簡單來說就是函數裡面的變量會被保存下來,你可以理解成全局變量。)
…………………………………………………………………………………… 當 當 當 ................. 下面有請我們的幣包同志
function a(){ var aa = 0; function b(){ aa ++; console.log(aa); } return b; } var ab = a(); ab(); //1 ab(); //2
看到了吧裡面的變量的值沒有被銷毀,因為函數a被外部的變量ab引用,所以變量aa沒有被回收。
如果某個函數被它的父函數之外的一個變量引用,就形成了一個閉包
還有一種更為常用的閉包寫法
var bi = (function(){ var a = 0; function b(){ a ++; console.log(a); } return b; })(); bi(); //1 bi(); //2 bi(); //3
執行過程分析:
首先把一個自執行函數賦值給了bi,這個自執行函數運行完成以後就bi的值就變成了
function b(){ a ++; console.log(a); }
因為我們在上面的代碼return回去了b,然後因為這個自執行函數被bi引用所以裡面的變量a並沒有因為這個自執行函數執完而銷毀,而是保存到了內存中,所以我們多次打印bi()就成了1、2、3
下面我來說一個閉包的使用場景吧。
沒有使用閉包的版本
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); //不管我怎麼點都是返回6 } } }
使用了閉包的版本
window.onload = function(){ var ul = document.getElementsByTagName("ul")[0]; var li = ul.getElementsByTagName("li"); for(var i=0;i<li.length;i++){ (function(i){ li[i].onclick = function(){ console.log(i); //點擊第幾個返回第幾個 } })(i) } }
、、、、、如果你不理解這個,你只要這樣子用就能夠達到效果。
這也只是簡單的介紹了一下,後面將會在閉包的高級部分講解。如果你對閉包有更深的理解可以pm我。