原文地址:http://tobyho.com/2011/11/02/callbacks-in-loops/

某些时候,你需要在循环里创建一个回调函数。我们来试试给页面里每个链接增加点击事件。

var links = documnet.getElementsByTagName('a')
for (var i = 0, len = links.length; i < len; i++){
// Note: `addEventListener` is standard compliant browsers only
links[i].addEventListener('click', function(e){
alert('You clicked on link ' + i)
}, false)
}

当你点击链接的时候,你将打开一个警告框告诉你被点击的链接索引。很好用吧,但是,这不会发生。

实际上,每次警告框都会弹出"You clicked on link 5" - 假如你有5个链接。发生这样的原因是你创建的每一个回调函数(每个循环迭代)都指向了相同的变量i。也就是说,尽管var i在循环内被定义,但他不在循环范围内,即新的i并不是被循环构造出的。其实,我就算在循环外声明,也是一样的结果。

 var links = documnet.getElementsByTagName('a')
var i
for (i = 0, len = links.length; i < len; i++){
/* blah blah */
}

解决这个问题的方法是创建一个outer函数,并传递参数i来了-然后立刻执行它。

 var links = documnet.getElementsByTagName('a')
for (var i = 0, len = links.length; i < len; i++){
!function outer(i){
links[i].addEventListener('click', function inner(e){
alert('You clicked on link ' + i)
}, false)
}(i)
}

注:我还定义了回调处理函数inner,以区分这两个函数。

outer的功能也被称为IIFE - 它被创建后立即执行,然后销毁。 (还有其他的方法来写 - 我只是喜欢的版本的!)我们需要它是因为我们需要一个新的变量范围,并在Javascript中,函数是唯一能这么做的办法。

要注意在outer函数外部,i仍然是循环中定义的i,但在inside函数内,i只是被声明的一个局部变量, - 它优先于outside函数中的i。

 !function(i){  // <-- inside `i`
/* here, `i` refers to inside `i` */
}(i) // <-- still outside `i`

我们可以将inside函数中的变量i改成ii,这样看起来清楚一点。

 var links = documnet.getElementsByTagName('a')
for (var i = 0, len = links.length; i < len; i++){
!function outer(ii){
links[i].addEventListener('click', function inner(e){
alert('You clicked on link ' + ii)
}, false)
}(i)
}

现在ii变量在outer函数里,变量i在外面。因为闭包,ii将一直存在直到outer函数结束(单击事件发生的时候)。换句话说,inner函数将ii变量固定,任何代码都可以访问。

更新

Jonprins评论说道,创建一个函数的开销是昂贵的,所以outer函数最好在循环之外。我同意这个观点,我同意。现在优化后的代码如下。

 function addClickHandler(link, i){
link.addEventListener('click', function(e){
alert('You clicked on link ' + i)
}, false)
}
var links = documnet.getElementsByTagName('a')
for (var i = 0, len = links.length; i < len; i++)
addClickHandler(links[i], i)

这下代码看起来又干净又有效率了。

结论


你现在已经一点点的知道了闭包是如何工作的,并注意到循环里的回调函数的问题所在。如果你有任何疑问,请给我留言。

JS闭包文章--(翻译)Callbacks in Loops的更多相关文章

  1. js闭包的作用

    js闭包的用途详解 js闭包可以用在许多地方.它的最大用处有两个,一个是前面提到的可以读取函数内部的变量,另一个就是让这些变量的值始终保持在内存中.具体怎么理解呢,各位看官请仔细看好下文   我们来看 ...

  2. 一篇文章看懂JS闭包,都要2020年了,你怎么能还不懂闭包?

     壹 ❀ 引 我觉得每一位JavaScript工作者都无法避免与闭包打交道,就算在实际开发中不使用但面试中被问及也是常态了.就我而言对于闭包的理解仅止步于一些概念,看到相关代码我知道这是个闭包,但闭包 ...

  3. 大部分人都会做错的经典JS闭包面试题

    由工作中演变而来的面试题 这是一个我工作当中的遇到的一个问题,似乎很有趣,就当做了一道题去面试,发现几乎没人能全部答对并说出原因,遂拿出来聊一聊吧. 先看题目代码: function fun(n,o) ...

  4. Js闭包的用途

    本来想总结一点JavaScript中的闭包的一些用法,在查资料的时候发现了一篇很好的文章,就转过来收藏了,下面附上传送门: js闭包的用途 ---------sunlylorn 我们来看看闭包的用途. ...

  5. 揭开JS闭包的面纱

    今天看了关于js闭包方面的文章,还是有些云里雾里,对于一个菜鸟来说,学习闭包确实有一定的难度,不说别的,能够在网上找到一篇优秀的是那样的不易. 当然之所以闭包难理解,个人觉得是基础知识掌握的不牢,因为 ...

  6. js闭包实例汇总

    本文是通过实例来帮助大家深刻理解js闭包,是篇非常不错的文章,这里推荐给大家,有需要的小伙伴可以参考下 Js闭包 闭包前要了解的知识 1. 函数作用域 (1).Js语言特殊之处在于函数内部可以直接读取 ...

  7. 关于js闭包之小问题大错误

    闭包是 JavaScript 开发的一个关键方面:匿名函数可以访问父级作用域的变量. 如果闭包的作用域中保存着一个 HTML 元素,则该元素无法被销毁.(下面代码来自高程) 刚看到一个关于闭包自己没注 ...

  8. js闭包之我见

    很久前的一个问题终于得以解决,内心是无比喜悦的,不多说,先上代码: function test(){ for(var i=0;i<5;i++){ window.onclick=function( ...

  9. js闭包引起的事件注册问题

    背景:闲暇时间看了几篇关于js作用域链与闭包的文章,偶然又看到了之前遇到的一个问题,就是在for循环中为dom节点注册事件驱动,具体见下面代码: <!DOCTYPE html> <h ...

随机推荐

  1. Spring 通过XML配置文件以及通过注解形式来AOP 来实现前置,环绕,异常通知,返回后通知,后通知

    本节主要内容: 一.Spring 通过XML配置文件形式来AOP 来实现前置,环绕,异常通知     1. Spring AOP  前置通知 XML配置使用案例     2. Spring AOP   ...

  2. 一个初学者对于MVC架构的理解

    我很早之前就开始接触.NET开发,一直都在2.0的框架下,所以对于MVC这种架构,听说过,但没有具体使用过,近期和外部朋友接触时,有了解到他们公司在使用MVC这种架构,所以自己就找来相关资料了解一下M ...

  3. A python tool to static sim.log duration time

    When working ALU IMS Patch team, we need to static the SU duration to add it to the patch report, th ...

  4. sql server 2005 32位+64位、企业版+标准版、CD+DVD 下载地址大全 .

    企业版DVD SQL Server 2005 Enterprise Edition(支持超大型企业) 32 位DVD: ed2k://|file|cs_sql_2005_ent_x86_dvd.iso ...

  5. WCF 删除队列

    Configuration config = ConfigurationManager.OpenExeConfiguration (ConfigurationUserLevel.None); Serv ...

  6. Hadoop 1.0 和 2.0 中的数据处理框架 - MapReduce

    1. MapReduce - 映射.化简编程模型 1.1 MapReduce 的概念 1.1.1 map 和 reduce 1.1.2 shufftle 和 排序 MapReduce 保证每个 red ...

  7. 翻译《Writing Idiomatic Python》(一):if语句、for循环

    开篇废话 这是在美国Amazon上评价很不错的一本书,其实严格来说这可能不算书,而是一本小册子.就像书名一样,里面的内容主要是用一些例子讲述地道的Python的代码是怎样写的.书中把很多例子用不良风格 ...

  8. POJ 3416 Crossing --离线+树状数组

    题意: 给一些平面上的点,然后给一些查询(x,y),即以(x,y)为原点建立坐标系,一个人拿走第I,III象限的点,另一个人拿II,IV象限的,点不会在任何一个查询的坐标轴上,问每次两人的点数差为多少 ...

  9. uGUI VS NGUI

    前言 这篇日志的比较是根据自己掌握知识所写的,请各路大神多多指教. 引擎版本: Unity 4.6 beta 两者区别 1.uGUI的Canvas 有世界坐标和屏幕坐标 2.uGUI的Button属性 ...

  10. Flash Builder 找不到所需的 Adobe Flash Player

    经测试该方法可用! http://bbs.9ria.com/thread-108472-1-1.html 最近重装了系统,flash开发工具也由flex换成了flash builder.调试时就出现了 ...