原文地址: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. 问题解决——使用GP-3120TN打印条形码标签

    终于大致的尝试出了参数和编程手册里指令的使用. 在这里,感谢佳博中一个叫做"Gprinter 陶玮"的工程师所提供的技术支持.非常感谢,如果没有你,在写这篇文章之前我可能换别的打印 ...

  2. 给Apache增加SSI支持(shtml的奥秘)

    什么是SSI? SSI是英文Server Side Includes的缩写,翻译成中文就是服务器端包含的意思.从技术角度上说,SSI就是在HTML文件中,可以通过注释行调用的命令或指针.SSI具有强大 ...

  3. 基于元数据的ETL系统

     从努力到选择  从实现到设计 从部分到整体 以下是我对DW design的一些想法 下次使用C#来实现一下   ETL中Source 的信息       数据提供形式:DB(ORACLE SQLSE ...

  4. 深搜+DP剪枝 codevs 1047 邮票面值设计

    codevs 1047 邮票面值设计 1999年NOIP全国联赛提高组  时间限制: 1 s  空间限制: 128000 KB  题目等级 : 钻石 Diamond 题目描述 Description ...

  5. AC日记——神奇的幻方 洛谷 P2615(大模拟)

    题目描述 幻方是一种很神奇的N*N矩阵:它由数字1,2,3,……,N*N构成,且每行.每列及两条对角线上的数字之和都相同. 当N为奇数时,我们可以通过以下方法构建一个幻方: 首先将1写在第一行的中间. ...

  6. dexDebug ExecException finished with non-zero exit value 2

    Error:Execution failed for task ':app:transformClassesWithDexForDebug'. com.android.build.api.transf ...

  7. Android优化——UI优化(一)优化布局层次

    优化布局层次 1.避免布局镶嵌过深(如下) <LinearLayout xmlns:android="http://schemas.android.com/apk/res/androi ...

  8. 实现了一个简单的cage变形器

    今天实现了一个简单变形器,可以用一个网格的形状影响另一个网格的形状. 如图,蓝色网格的形状被灰色网格操控. 当前的算法非常简单,就是计算蓝色网格每个点到灰色网格每个点的距离,以距离x次方的倒数作为权重 ...

  9. w3school-CSS

    1.与XHTML不同,CSS对大小写不敏感.但是,当与HTML一起工作的时候,class和id名称对大小写是敏感的. 2.body {.....};通过css继承关系,子元素将继承最高级元素(本例是b ...

  10. XCode的 Stack Trace,调试时抛出异常,定位到某一行代码

    在Xcode调试程序的时候,总是会出现不知道错误在什么地方的问题,很是捉急,现在又一个办法,可以具体定位到错误行的代码,试一下吧?超级好用 操作很简单: 1.在XCode界面中按cmd + 6快捷键, ...