如何给循环中的对象添加事件--深入理解JavaScript的闭包特性
初学者经常碰到的,即获取HTML元素集合,循环给元素添加事件。在事件响应函数中(event handler)获取对应的索引。但每次获取的都是最后一次循环的索引。原因是初学者并未理解JavaScript的闭包特性。 有个网友问了个问题,如下的html,为什么点击所有的段落p输出都是5,而不是alert出对应的0,1,2,3,4。 1. <!DOCTYPE HTML> 2. <html> 3. <head> 4. <meta charset="utf-8" /> 5. <title>闭包演示</title> 6. <style type="text/css"> 7. p {background:gold;} 8. </style> 9. <script type="text/javascript"> 10.function init() { 11. var pAry = document.getElementsByTagName("p"); 12. for( var i=0; i<pAry.length; i++ ) { 13. pAry[i].onclick = function() { 14. alert(i); 15. } 16. } 17.} 18.</script> 19.</head> 20.<body onload="init();"> 21.<p>产品 0</p> 22.<p>产品 1</p> 23.<p>产品 2</p> 24.<p>产品 3</p> 25.<p>产品 4</p> 26.</body> 27.</html> 以上场景是初学者经常碰到的。即获取HTML元素集合,循环给元素添加事件。在事件响应函数中(event handler)获取对应的索引。但每次获取的都是最后一次循环的索引。 原因是初学者并未理解JavaScript的闭包特性。通过element.onclick=function(){alert(i);}方式给元 素添加点击事件。响应函数function(){alert(i);}中的 i 并非每次循环时对应的 i(如0,1,2,3,4)而是循环后最后 i 的值5。 或者说循环时响应函数内并未能保存对应的值 i,而是最后一次i++的值5。 了解了原因,下面就由几种方式可与解决: 1、将变量 i 保存给在每个段落对象(p)上 1. function init1() { 2. var pAry = document.getElementsByTagName("p"); 3. for( var i=0; i<pAry.length; i++ ) { 4. pAry[i].i = i; 5. pAry[i].onclick = function() { 6. alert(this.i); 7. } 8. } 9. } 2、将变量 i 保存在匿名函数自身 1. function init2() { 2. var pAry = document.getElementsByTagName("p"); 3. for( var i=0; i<pAry.length; i++ ) { 4. (pAry[i].onclick = function() { 5. alert(arguments.callee.i); 6. }).i = i; 7. } 8. } 3、加一层闭包,i 以函数参数形式传递给内层函数 1. function init3() { 2. var pAry = document.getElementsByTagName("p"); 3. for( var i=0; i<pAry.length; i++ ) { 4. (function(arg){ 5. pAry[i].onclick = function() { 6. alert(arg); 7. }; 8. })(i);//调用时参数 9. } 10.} 4、加一层闭包,i 以局部变量形式传递给内层函数 1. function init4() { 2. var pAry = document.getElementsByTagName("p"); 3. for( var i=0; i<pAry.length; i++ ) { 4. (function () { 5. var temp = i;//调用时局部变量 6. pAry[i].onclick = function() { 7. alert(temp); 8. } 9. })(); 10. } 11.} 5、加一层闭包,返回一个函数作为响应事件(注意与3的细微区别) 1. function init5() { 2. var pAry = document.getElementsByTagName("p"); 3. for( var i=0; i<pAry.length; i++ ) { 4. pAry[i].onclick = function(arg) { 5. return function() {//返回一个函数 6. alert(arg); 7. } 8. }(i); 9. } 10.} 6、用Function实现,实际上每产生一个函数实例就会产生一个闭包 1. function init6() { 2. var pAry = document.getElementsByTagName("p"); 3. for( var i=0; i<pAry.length; i++ ) { 4. pAry[i].onclick = new Function("alert(" + i + ");");//new一次就产生一个函数实例 5. } 6. } 7、用Function实现,注意与6的区别 1. function init7() { 2. var pAry = document.getElementsByTagName("p"); 3. for( var i=0; i<pAry.length; i++ ) { 4. pAry[i].onclick = Function('alert('+i+')'); 5. } 6. } |
浅析Javascript闭包的特性
2009-07-24 17:30 司徒正美 cnblogs 我要评论(1) 字号:T | T
本文将对Javascript闭包的特性进行分析,并举例进行说明。闭包,是指语法域位于某个特定的区域,具有持续参照(读写)位于该区域内自身范围之外的执行域上的非持久型变量值能力的段落。
AD:
Javascript闭包的定义非常晦涩——闭包,是指语法域位于某个特定的区域,具有持续参照(读写)位于该区域内自身范围之外的执行域上的非持久型变量值能力的段落。这些外部执行域的非持久型变量神奇地保留它们在闭包最初定义(或创建)时的值(深连结)。
简单来说,Javascript闭包就是在另一个作用域中保存了一份它从上一级函数或作用域取得的变量(键值对),而这些键值对是不会随上一级函数的执行完成而销毁。周爱民说得更清楚,闭包就是“属性表”,闭包就是一个数据块,闭包就是一个存放着“Name=Value”的对照表。就这么简单。但是,必须强调,闭包是运行期概念,一个函数实例。
Javascript闭包的实现,通常是在函数内部再定义函数,让该内部函数使用上一级函数的变量或全局变量。
ECMAScript认为使用全局变量是一个简单的Javascript闭包实例。
1. var sMessage = "Hello World";
2. function sayHelloWorld(){
3. alert(sMessage);
4. };
5. sayHelloWorld();
但它完成没有体现Javascript闭包的特性……
现在比较让人认同的Javascript闭包实现有如下三种
1. with(obj){
2. //这里是对象闭包
3. }(function(){
4. //函数闭包
5. })()try{
6. //...
7. } catch(e) {
8. //catch闭包 但IE里不行
9. }
附上今天在无忧看到的问题:
要求:
让这三个节点的Onclick事件都能正确的弹出相应的参数。
1. <ul>
2. <li id="a1">aa</li>
3. <li id="a2">aa</li>
4. <li id="a3">aa</li>
5. </ul>
6. <script type="text/javascript">
7. <ul>
8. <li id="a1">aa</li>
9. <li id="a2">aa</li>
10. <li id="a3">aa</li>
11. </ul>
12. <script type="text/javascript">
13. for(var i=1; i < 4; i++){
14. var id = document.getElementById("a" + i);
15. id.onclick = function(){
16. alert(i);//现在都是返回4
17. }
18. }
19. </script>
客服果果的解答:
1. for(var i=1; i < 4; i++){
2. var id = document.getElementById("a" + i);
3. /*
4. 这里生成了一个匿名函数并赋值给对象 id_i;
5. */
6. id.onclick = function(){
7. /*
8. 这个i来源于局部变量,无法以window.i或者obj.i的形式在后期引用,
9. 只好以指针或者变量地址方式保存在这个匿名函数中,
10. 这就是传说的闭包,所以所有这个过程中生成的事件句柄都使用引用
11. 的方式来持久这个变量,也就是这些匿名函数共用一个变量i;
12. */
13. alert(i);
14. };
15. };
局部变全局
1. for(var i=1; i < 4; i++){
2. var id = document.getElementById("a" + i);
3. id.i=i;//这个i有了根
4. id.onclick=function(){
5. alert(this.i)
6. };
7. };1.for(var i=1; i < 4; i++){
8. var id = document.getElementById("a" + i);
9. window[id.id]=i;//这个i有了根
10. id.onclick=function(){
11. alert(window[this.id]);
12. };
13. }
产生一对一的更多Javascript闭包
1. for(var i=1; i < 4; i++){
2. var id = document.getElementById("a" + i);
3. id.onclick = new function(){
4. var i2=i;//这个i是闭包的闭包
5. return function(){
6. alert(i2);
7. }
8. };
9. }
如何给循环中的对象添加事件--深入理解JavaScript的闭包特性的更多相关文章
- 深入理解JavaScript的闭包特性如何给循环中的对象添加事件
初学者经常碰到的,即获取HTML元素集合,循环给元素添加事件.在事件响应函数中(event handler)获取对应的索引.但每次获取的都是最后一次循环的索引.原因是初学者并未理解JavaScript ...
- 深入理解JavaScript的闭包特性 如何给循环中的对象添加事件(转载)
原文参考:http://blog.csdn.net/gaoshanwudi/article/details/7355794 初学者经常碰到的,即获取HTML元素集合,循环给元素添加事件.在事件响应函数 ...
- JavaScript的闭包特性如何给循环中的对象添加事件(一)
初学者经常碰到的,即获取HTML元素集合,循环给元素添加事件.在事件响应函数中(event handler)获取对应的索引.但每次获取的都是最后一次循环的索引.原因是初学者并未理解JavaScript ...
- 给ul中的li添加事件的多种方法
给ul中的li添加事件的多种方法 这是一个常见,而且典型的前端面试题 <ul> <li>11111</li> <li>22222</li> ...
- React 函数组件中对window添加事件监听resize导致回调不能获得Hooks最新状态的问题解决思路
React 函数组件中对window添加事件监听resize导致回调不能获得Hooks最新状态的问题解决思路 这几天在忙着把自己做的项目中的类组件转化为功能相同的函数组件,首先先贴一份该组件类组件的关 ...
- JQuery中的对象和事件
一:JQuery 对象和 Dom 对象 在使用 JQuery 过程中,我们一般(也是绝大多数情况下,除非我们使用了第二个框架)只有两类对象,即:JQuery 对象和 Dom 对象.Dom 对象指的是普 ...
- OAF TABLE中第一列添加事件不生效
我遇到一个比较诡异的现象 在TABLE中,我在TABLE的第一列添加了一个MessageCheckBox,并为其设置全局刷新的FireAction事件selection, 但是点击勾选框按钮之后,事件 ...
- JavaScript中给对象添加方法
在JavaScript中,我们经常要给已定义的对象添加一些方法,如下: function circle(w,h){ this.width=w; this.height=h; ...
- 在CorelDRAW中为对象添加块阴影效果
我们可以使用CorelDRAW来绘制矢量图形,在勾画出简单的图形后,往往还需要对它们进行一些或简单或复杂的处理,以增加一定的艺术效果.CDR中可供选择的效果有很多,作用的对象可以是文字,也可以是图案. ...
随机推荐
- HDU-1012(水题)
http://acm.hdu.edu.cn/showproblem.php?pid=1012 分析:就按题目给的公式一步步输出就行了. #include<stdio.h> #include ...
- 关于js当中一些糟糕的特性
首先,不可否认,js是一门具有许多优秀特性的弱类型语言,但是这门语言在设计之初就投入了工程实践,没有经历严格的实验室测试,以致力于它是如此的粗糙,在相当长的一段时间很不受开发者待见,被视为一门玩具性的 ...
- 2015前端各大框架比较(angular,vue,react,ant)
前端流行框架大比拼 angular vue react ant-design angularjs angular是个MVVM的框架.针对的是MVVM这整个事.angular的最主要的场景就是单页应用, ...
- css标准导航代码
<!-- 例子解析: --> --> <!-- list-style-type:none - 移除列表前小标志.一个导航栏并不需要列表标记 --> <!-- 移除浏 ...
- 样式优先级、margin
margin:上 左 下 右:
- ios应用,今年最蛋疼的6月,IPV6!!
刚刚苹果大会结束,你是不是后悔没去听他的发布会,!!有钱么你? iPV6 国人蒙蔽了,介是什么鬼,经过两三次的残忍拒绝,我认真去研究了iPV6, 2.2 Details We discovered ...
- leetcode中一些要用到动态规划的题目
需要仔细回顾的题目: 1.Interleaving String 交叉存取字符串 2.Decode Ways 字符串解码 3.Subsets Subsets II 求一个 ...
- 要想重启后也生效LINUX防火墙配置
新配置的一台服务器,安装的是CentOS6.3系统,在安装完LNMP之后,发现nginx进程存在,且php解析正常,但是用分配的独立IP去访问的时候发现无法访问. 查了下网上的资料,发现可能是Linu ...
- 『重构--改善既有代码的设计』读书笔记----Change Reference to Value
如果你有一个引用对象,很小且不可改变,而且不易管理,你就需要考虑将他改为一个值对象.在Change Value to Reference我们说过,要在引用对象和值对象之间做选择,有时候并不容易,有了重 ...
- CoffeeScript飞一样的写javascript
之前看到同事在使用coffeescript写js,当我看到那简介的coffee文件,就深深的被coffescript吸引了,简洁的语法,熟练之后会大大提升javascript的开发速度,写脚本也能像飞 ...