<html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> </head> <body> <button class="btn1"> 踩坑1 </button> <button class="btn1"> 踩坑2 </button> <button class="btn1"> 踩坑3 </button> <button class="btn1"> 踩坑4 </button> <script> var buttons1 = document.getElementsByClassName('btn1'); /* (function(){ for(var i=0; i<buttons1.length; i++){ var btn = buttons1[i]; btn.onclick = function(){ alert(i); } } })(); */ //上面這段代碼看上去是沒問題的,爲何會每次點擊都是4呢 //換個寫法,上面的自執行函數等價於下面這種寫法 //討論下閉包是什麼 function bindBtn1(){ for(var i=0; i<buttons1.length; i++){ var btn = buttons1[i]; /*Javascript中,沒有塊級做用域,只有函數做用域。因此在函數中定義的變量,
是隻能夠在函數內部被訪問到的,包括在函數內部定義的子函數。 因此內部函數clickFunc能訪問到父函數的變量 i */ var clickFunc = function(){ alert(i); } /*若是內部函數存在被外部調用的可能,
那麼內部函數能訪問到的外部函數的成員變量是不該該被Javacript引擎回收掉內存空間的。 在這裏clickFunc有被外部btn1點擊訪問的可能,
因此他的父函數bindBtn1的內存空間在執行完成後是不會被回收內存的, 所以clickFunc繼續享有了父函數bindBtn1做用域的變量訪問權限, *而且這個函數做用域在外部是不能被外界訪問到的,
是封閉的,被內部函數clickFunc所獨享的,所以bindBtn1造成了一個閉包。 *從本質上來講,閉包的造成是利用了Javacript內存回收機制,
獲得的一個封閉的內存塊,這與Java中的一個對象是否是十分類似呢 */ btn.onclick = clickFunc; //這是內部函數的一個偉大的逃脫 } } bindBtn1(); var viewbtn1 = buttons1; //那麼來分析一下爲何,點擊踩坑按鈕每次都會輸出同一個數字,而不是遞增的數字呢 //調試看看button1[0] 的onclick函數 /* onclick : function arguments : null caller : null length : 0 name : 'clickFunc' ...... ###########看看做用域########### [[scope]] : scopes[2] 0 : Closure (bindBtn1) * 閉包做用域 i = 4 i : 4 1 : Globle * 全局做用域 ... ################################ */ /************************************************************************ 重點來了:注意 Closure (bindBtn1) * 閉包做用域 i = 4 i : 4 內部函數的做用域除了全局做用域外,還有bindBtn這個閉包做用域。 原來是這樣,這幾個內部函數 clickFunc 共用了一個閉包做用域啊!!! 因此能訪問到的 i 實際上是一個。 因此每次點擊按鈕顯示的 i 永遠是同樣的。 那麼如何解決這個問題呢,看看下面吧 *************************************************************************/ </script> <br/> <button class="btn2"> 填坑1 </button> <button class="btn2"> 填坑2 </button> <button class="btn2"> 填坑3 </button> <button class="btn2"> 填坑4 </button> <script> var buttons2 = document.getElementsByClassName('btn2'); function bindBtn2(){ for(var i=0; i<buttons2.length; i++){ //要解決這個問題,咱們須要讓內部函數不能共享同一做用域 //因此爲每一個內部函數建立不一樣的閉包做用域 /* (function(j){ var btn = buttons2[j]; btn.onclick = function(){ alert(j); } })(i); */ //上面代碼等價於下面的 : function selfClosure(j){ var btn = buttons2[i]; var clickFunc = function(){ alert(j); } btn.onclick = clickFunc; } selfClosure(i); } } bindBtn2(); var viewbtn2 = buttons2; /************************************************************************ 調試 buttons2[0] 的onclick函數 onclick : function arguments : null caller : null length : 0 name : 'clickFunc' ...... ###########看看做用域########### [[scope]] : scopes[3] 0 : Closure (selfClosure) * 閉包做用域 selfClosure j : 0 1 : Closure (bindBtn1) * 閉包做用域 bindBtn1 i : 4 2 : Globle * 全局做用域 ... ################################ 如今每一個內部函數都有了私有的selfClosure閉包做用域,
獨享做用域中的j變量,因此就解決了上述問題。 *************************************************************************/ </script> </body> <html>