關於閉包的一些知識
在瞭解閉包之前先得弄清楚之前講的作用域的相關知識。
1、什麼是閉包
可以在外部通過某一種手段(方法)可以訪問到內部的變數,這種方法就叫做閉包。
2、閉包用法的簡單舉例
function fun1(){ var a = 100; } fun1()
console.log(a);
如上程式碼所示,依我們所學的知識我們此時想要打印出a的值應該不行的,因為a定義的作用域在函式內,全域性的GO上是沒有a這個變數的
那麼我們怎麼樣才能讓它在外部打印出來呢
function fun1(){ var a = 100; function fun2(){ console.log(a); } return fun2; } var fun = fun1(); fun()
如上所示,我們先將我們所要執行的程式碼封裝在一個函式內部的另一個函式裡,然後返回這整個函式並在外部接受然後觸發,這樣就能做到我們想要的效果。
那麼,原理是什麼呢?
首先我們分析一下函式的作用域:
在fun1執行時,生成fun1所對應的作用域,fun1.AO = {a:100,fun2:function}(此處省略分析過程)
在fun2被返回執行的時候,生成fun2所對應的作用域,fun2.AO = { }
這時關鍵點在於,返回出來的fun2這整個函式就算返回到了fun1外面,但是fun2的父級依然還是fun1,那麼在fun2的AO中找不到a對應的值,就返回父級的AO中尋找其所對應的值,而fun1的AO中有a,值為100。
所以這是fun2被返回呼叫後執行打印出來的結果就是100。
3、那麼我們再看一個情況:
function fun1(){ var a = 100; function fun2(){ a ++; console.log(a); } return fun2; } var fun = fun1(); fun() fun()
如上所示,根據之前的知識,我們知道這裡當fun第一次呼叫時列印的結果為101,那麼當fun第二次呼叫的時候呢?
因為fun2的AO中並沒有定義a,所以fun2呼叫時改變的a的值是在其父級也就是fun1的AO中改變a的值,所以當fun第二次被呼叫的時候,此時fun1中a的值已經變為101,故第二次呼叫完後打印出來的值為102。
4、這裡我們再討論一種做法:
<ul> <li>0</li> <li>1</li> <li>2</li> <li>3</li> <li>4</li> <li>5</li> <li>6</li> <li>7</li> <li>8</li> <li>9</li> </ul> <script type="text/javascript"> var lis = document.getElementsByTagName("li"); for(var i = 0;i < lis.length;i++){ lis[i].onclick = function(){ console.log(i) } } </script>
如上所示,我們想要點選某一個數時後臺打印出其對應的數字,粗略來看,上述邏輯好像並沒有什麼問題,但不管點選什麼打印出來都只是10.
那麼這是為什麼呢?
因為在我們點選之前,全域性已經生成了作用域GO,此時因為迴圈已經完成,生成的GO中變數i的最後賦值肯定是為10,然而當我們點選觸發時間函式時所生成的作用域AO中沒有關於i的定義,只能返回父級尋找,所以這時找到的i值就為10.
那麼怎麼解決呢?
<ul> <li>0</li> <li>1</li> <li>2</li> <li>3</li> <li>4</li> <li>5</li> <li>6</li> <li>7</li> <li>8</li> <li>9</li> </ul> <script type="text/javascript"> var lis = document.getElementsByTagName("li"); for(var i = 0;i < lis.length;i++){ (function(i){ lis[i].onclick = function(){ console.log(i) } })(i) } </script>
如上所示,將點選事件函式裝在一個立即執行函式中,形參實參都為i,這時每一次迴圈所生成的函式引數都為列表所對應的值,那麼這所生成的所有函式的AO中都會有其所對應的i值,而這些函式正是我們點選事件函式的父級,所以這時我們再去點選觸發時返回父級所尋找的i值即為我們想要的值。
以上簡單的列舉了閉包的一些簡單用法,閉包具體的應用還有很多很多,學無止境。