問題描述
我們都知道,如果我們對於一組元素(相同的標簽)同時進行onclick事件處理的時候(在需要獲取到索引的時候),一般是寫一個for循環,但是onclick是一個異步調用的,所以會帶來一個問題,當我們觸發這個事件的時候,我們能獲取的i值是for完整執行完后i的值,而不能獲取到代碼順序里i的值
首先看一段代碼:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title></title> </head> <body> <button>0</button> <button>1</button> <button>2</button> <button>3</button> <button>4</button> <script> var btn=document.getElementsByTagName('button'); for(var i=0;i<btn.length;i++){ btn[i].onclick=function(){ console.log(i); } } </script> </body> </html>
然后運行一下,當時作為一名天真的js初學者理所當然的認為分別點擊不同的額按鈕,會分別的打印出0、1、2、3、4,然而事與願為,現實和理想總是存在差距,點擊不同的按鈕,打印卻都是5,百思不得其解,不科學啊!
最后百度了一番才恍然大悟,原來console.log(i)里的i在循環完成的時候被賦值成了5,而每個按鈕的onclick都被賦值了同一個function,也就是說每個function里的i指的是同一個i,i=5,自然每個點擊都會打印出5,那么該怎么解決呢?!
利用閉包解決
再看接下來這段代碼:
<script> var btn=document.getElementsByTagName('button'); for(var i=0;i<btn.length;i++){ (function(n){ //這個是function里n,即function的形參,也可以換成j,換成什么變量名都無所謂 btn[n].onclick=function(){ console.log(i+'-'+n); //i總是等於5,而n則是點擊的數 } })(i);//這是循環中的i,被作為參數傳入 } </script>
再運行這段代碼,就可以得到你想要的效果,但是是為什么呢?
知道了原因就好辦了,利用閉包把每個function里的i都變成不同的i就行了,當時作為一名初學者還不懂閉包,也是后來才理解的。
循環中的function自調用,將循環中的i作為參數傳入function中,此時,function中的i已經不是循環中的i了(這里有點繞,其實形參i,即function里的i換成什么變量名都行),而是在內存中開辟了一個內存空間存儲了作為參數傳進來的i的值,這樣function中的就不會隨着循環中的i的值的改變而改變了,就可以打印出你要的結果了。
原理:
閉包的局部作用域。由於這個你們函數總是和代碼執行順序一樣的,所以n可以實時記錄i的值。由於for每執行一次,就執行一次匿名函數,每一次執行有自己的執行環境,有着自己的作用域鏈,所以用這函數里面一個變量來記錄一下實時的i的值,這個n是不會隨着i的改變而改變的