zoukankan      html  css  js  c++  java
  • JS闭包文章--(翻译)Callbacks in Loops

    原文地址: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并不是被循环构造出的。其实,我就算在循环外声明,也是一样的结果。

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

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

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

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

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

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

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

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

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

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

    更新

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

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

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

    结论


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

  • 相关阅读:
    为什么你不会redis分布式锁?因为你没看到这篇文章
    JavaScript最佳做法—创建对象
    js构造函数的定义
    作用域和闭包
    js中的call()和apply()的区别
    3种方法快速查找两个数组是否在Javascript中包含任何公共项
    JavaScript继承的6种方式以及它们的优缺点
    JS常见的内存泄漏及可用的解决方法
    Vuex简单入门
    如何使用HTML和CSS为背景创建Wave图片?
  • 原文地址:https://www.cnblogs.com/lazio10000/p/3567269.html
Copyright © 2011-2022 走看看