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