JS闭包致使循环给按钮添加事件时老是执行最后一个

就是有不定个的按钮,且点击按钮时都须要执行一个方法(参数不同)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,总算正确了...

相关文章
相关标签/搜索