原文地址: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)

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

结论


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

最新文章

  1. JQuery Mobile入门——设置后退按钮文字(转)
  2. JAVA thread0.interrupt()方法
  3. [CareerCup] 18.5 Shortest Distance between Two Words 两单词间的最短距离
  4. JSP 九个隐含JSP对象
  5. 基于tcpdump实例讲解TCP/IP协议
  6. CRM报表打印
  7. SUDT2177体检
  8. 删除一个目录和其各级子目录下的.svn文件
  9. 利用case when 减少表扫描次数
  10. Linux分配给该用户没有权限登陆
  11. cocos2d-x3.0 经常使用绘图方法
  12. Swift和Javascript的神奇魔法
  13. Android Studio教程10-Intent的详细使用
  14. fastjson对象转为json字符串日期格式变为时间戳问题
  15. E - Intervals 贪心
  16. HTTP请求行、请求头、请求体详解(转)
  17. liblas 1.8.1编译安装
  18. SPF难以解决邮件伪造的现状以及方案
  19. 【Matplotlib】概要总览第一讲
  20. [javaSE] IO流(RandomAccessFile)

热门文章

  1. 烂泥:KVM虚拟机克隆
  2. spring mvc 配置文件拦截器过滤url
  3. Altium Designer PCB制作入门实例
  4. GoLang 的 daemonize 实现
  5. 深度优先搜索 codevs 1065 01字符串
  6. python中的getattr函数
  7. 如何利用python模仿浏览器进行网页爬取?
  8. Android代码优化----Application节点的模板写法及UI工具类
  9. Android service ( 一 ) 三种开启服务方法
  10. 堡垒机环境-jumpserver部署