原文地址: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. 烂泥:SQL Server 2005数据库备份与恢复

    本文由秀依林枫提供友情赞助,首发于烂泥行天下. 服务器的迁移,因为业务使用的数据库是SQL Server 2005,所以就要首先对数据库进行备份. 备份可以分为手动备份和自动备份,我们先来介绍手动备份 ...

  2. JS高级程序设计2nd部分知识要点5

    JS Regexp 字面量模式 用\反斜杠转义 构造函数中的字符串 也用\转义正则也用\ RegExp实例属性 global -布尔值  /g ignoreCase -布尔值 /i lastIndex ...

  3. python 标准库和第3方库的介绍

    忘了从哪里来的了~~~~ Tkinter———— Python默认的图形界面接口.Tkinter是一个和Tk接口的模块,Tkinter库提供了对Tk API的接口,它属于Tcl/Tk的GUI工具组.T ...

  4. linux alarm函数解除read write等函数的阻塞

    看到apue的第十章,说到alarm,pause可以实现sleep,可以让某些一直阻塞的函数超时,例如read,write.代码如下: static void sig_alrm(int signo) ...

  5. HADOOP cluster some issue for installation

    给namenode搭建了HA,然后根据网上的配置也配置了secondary namenode, 但是一直没有从日志中看到启动secondnary namenode,当然进程也没有. 找了很多资料,按照 ...

  6. Neo4j 高可用集群安装

    安装neo4j高可用集群,抓图安装过程 http://www.ibm.com/developerworks/cn/java/j-lo-neo4j/ Step1.下载neo4j商业版并解压,复制为neo ...

  7. Trie树 & 01Trie

    指针版 #define MAXNUM 26 //定义字典树结构体 typedef struct Trie { bool flag;//从根到此是否为一个单词 Trie *next[MAXNUM]; } ...

  8. UESTC 31 饭卡(Card) --背包问题

    背包问题. 思路:如果m<5,此时也不能消费,所以此时答案为m m>=5: 求出背包容量为m-5,买前n-1样便宜的菜(排个序)的最大价值(即最大消费,即消费完后剩余值最接近5)最后减去最 ...

  9. MySQL数据库学习笔记(二)----MySQL数据类型

    ​[声明] 欢迎转载,但请保留文章原始出处→_→ 生命壹号:http://www.cnblogs.com/smyhvae/ 文章来源:http://www.cnblogs.com/smyhvae/p/ ...

  10. Mobile Prototype Dev Res Collection(Unity原型开发资源储备)

    资源储备 本文针对mobile原型开发阶段的资源收集 在做移动端的开发时,当有灵感想做些东西时,若是此时缺少美术资源和可用的脚本,此刻会有些纠结,今天在Assets Store上Mark了一些移动端开 ...