就是有不定個的按鈕,且點擊按鈕時都須要執行一個方法(參數不同)javascript
那麼我很天然的就想到了,循環給每一個按鈕添加事件和參數就行了,因爲不方便上傳系統代碼,下面以一個簡單例子來講明.css
<!DOCTYPE HTML> <html> <head> <meta charset="utf-8"/> <title>閉包循環問題</title> <style type="text/css"> p {background:red;} </style> </head> <body> <p class="p">我是1號</p> <p class="p">我是2號</p> <p class="p">我是3號</p> <p class="p">我是4號</p> <p class="p">我是五號</p> </body> </html> 閉包循環問題
好比如今要實現這麼一個功能,在頁面上點擊上面的按鈕1到按鈕5時分別alert出1,2,3,4,5.html
那麼不少人天然想到以下這麼作:java
加入以下腳本代碼:windows
<script type="text/javascript"> var page = document.getElementsByTagName("p");for(var i=0; i<page.length; i++) { page[i].onclick = function () { alert("我是"+i+"號"); } } </script>
運行後,奇怪的發現不管點擊那個li標籤,alert出的都是最後一個的內容-"我是5號"閉包
你無論點擊哪個,都alert」我是5號「;
緣由就是你循環了五次產生了五個閉包,而這5個閉包共享一個變量i,說的明白一點就是,在for循環結束時,只是把這五個匿名函數註冊給click事件,當時在循環的時候並無執行,當循環結束了,此時i的值是5;以後你去點擊p標籤,你點擊哪個就執行哪個對應的匿名函數(這個時候才執行),這時候匿名中發現一個i,匿名中沒有定義i,因而沿着做用域鏈找,找到了,可是這時候循環早就結束了,i等於5,因而彈出」我是5號「來;點擊其餘的同理;app
怎麼解決呢函數
既然已經知道函數調用外部變量的時候就構成了一個閉包,裏面的變量會受到別的地方的影響,那麼咱們
post
如今要作的就是,構建一個只有本身自己纔可訪問的閉包,保存只供自己使用的變量測試
構建一個閉包很簡單,代碼以下:
方式一:
<script> var list_obj = document.getElementsByTagName('p'); for (var i = 0; i <= list_obj.length; i++) { list_obj[i].onclick = (function(i){ // outer function return function(){ //inner function alert(i); }; })(i); } </script>
方式二:
<script> var list_obj = document.getElementsByTagName('p'); for (var i = 0; i <= list_obj.length; i++) { (function(i){ //var p = i list_obj[i].onclick = function() { alert(i); } })(i); } </script>
方法三:
<script> var page = document.getElementsByTagName("p");for(var i=0; i<page.length; i++) { page[i].num = i//先把每一個變量值存起來 page[i].onclick = function () { alert("我是"+this.num+"號"); } } </script>
閉包中的"this"對象:
<script> var num = 1; var obj = { num:2, getNum:function() { return function () { return this.num; } } } alert(obj.getNum()());//num -> 1 </script>
爲何不彈出2呢,這裏是說明閉包中你須要注意如今的this的指向那一個對象,其實記住一句話就永遠不會用錯this的指向問題,this永遠指向調用它的做用域;
若是這樣寫你就可能理解了
<script> var num = 1; var obj = { num:2, getNum:function() { return function () { return this.num; } } } var a = obj.getNum(); alert(window.a());//1 </script>
實際上是window對象調用的,this指的是windows,這就是說閉包中的this讓你看不清this的指向;
要是讓它alert 2你要這樣:
<script> var num = 1;var obj = { num:2, getNum:function() { var _this = this;//在這裏保存this return function () { return _this.num; } } } var a = obj.getNum(); alert(window.a()); </script>
其實,對於閉包沒有太多的概念,也沒有深刻的去研究,把本身在項目中碰到的一個例子拿出來寫寫:
具體頁面展現是這樣的:循環生成動態菜單,而後給每一個生成的菜單添加對應的onclick事件,
在代碼中是這麼寫的:
html:代碼 <a id="bb" href="javascript:void(0);" class="easyui-menubutton" data-options="menu:'#layout_north_stMenu222',iconCls:'icon-cologne-sign-out'">導出</a> <div id="layout_north_stMenu222" style="width: 150px; display: none;"> <div class="menu-sep"></div> <div id="_download_1" data-options="iconCls: 'icon-hamburg-docs'">導出Excel</div> <div id='mdm'>到工程//在"findItem"中是根據div中的文原本的查找的 <div id='cprjList'></div> </div> JS代碼: $.post(url,data,function(data){ if (data.rows && data.total>0) { var menubutton = $($('#bb').menubutton('options').menu);//得到下拉菜單的button var menu = menubutton.menu('findItem','到工程');//得到菜單項'到工程',這個是div的名字 for(var i=0;i<data.total;i++)////data.rows就兩條記錄,因此i=0,i=1 { menubutton.menu('appendItem',{ parent:menu.target,//給"到工程"這個菜單項動態增長子項 text:data.rows[i].name, iconCls: 'icon-hamburg-docs', //onclick:function(){accoToCprj(console.info(i);data.rows[i].id,data.rows[i].name);}開始的寫法 onclick:(function(i){//給返回的函數傳入參數i return function(){//因而在返回的這個函數的做用域就可使用參數i console.info(i); accoToCprj(data.rows[i].id,data.rows[i].name); }; })(i) }); } return; } })
開始的寫法是:
onclick:function(){accoToCprj(console.info(i);data.rows[i].id,data.rows[i].name);}
怎麼測試,發現彈出來的都是2,一直摸不着頭腦,網上搜了一下,才發現問題,由於生成菜單後,點擊觸發onclick事件,就會執行accoToCprj()方法,由於此時i已經不是0,也不是1,而是2,由於在循環過程當中,在i=0,i=1的時候生成兩個菜單,而後i=2的時候跳出循環了,因此在觸發onclick的時候,此時找到的i=2,因此一直彈出的是"2",因而在網上面找資料,把變量i存儲起來,在單擊的時候,返回一個函數,該返回的函數中能夠調用這個閉包中的變量i...在新寫的函數中,發現彈出的是0,1,總算正確了...