• DOM的addEventListener函数一个陷阱。


    时间:2015年7月9日

    这个函数是一个DOM Level3级方法,注册事件的,用法是:

    DOM对象.addEventListener("事件名称(比如click)",事件执行的函数(可以是匿名函数或者函数名),false/true(false表示冒泡方式,true表示捕获方式));

    这里注意第二个参数好像不是立即就编译进去(当然JS是没有编译的东西的,只是为了表述),而是在事件触发的时候才运行里边的代码,包括变量赋值。

    比如下边代码

    <a href="###">连接一</a>
    <a href="###">连接二</a>
    <a href="###">连接三</a>
    <a href="###">连接四</a>
    <a href="###">连接五</a>
    <a href="###">连接六</a>
    <a href="###">连接七</a>
    <a href="###">连接八</a>
    <a href="###">连接九</a>
    
    <script>
    var myHref = document.getElementsByTagName("a");
    for (var i = 0,mylength = myHref.length; i<mylength; i++) {
        myHref[i].addEventListener("click",function(e){
            e.preventDefault();
            alert(i);
        },"false");
    }
    </script>

    我们希望的结果是点击每一个a会弹出对应的数组标号值,也就是第一个a点击后显示0,第二个显示1。但是实际上我们发现并不是这个结果,而是总是显示最后一个a标签对象的数组标号值+1,也就是9。

    所以我认为,函数并不是立即执行,而只是发函数的引用放在那了,当事件发生的时候,才去调用实际的函数体,而且i做为全局变量,一直保存了下来,如果我们最后对i进行了别的操作,比如这样:

    <script>
    var myHref = document.getElementsByTagName("a");
    for (var i = 0,mylength = myHref.length; i<mylength; i++) {
        myHref[i].addEventListener("click",function(e){
            e.preventDefault();
            alert(i);
        },"false");
    }
    i=555;
    </script>

    每一个连接弹出的就是555了。实际i是做为全局变量的(js没有块作用域,所以for循环内的i不是局部变量,而是全局变量),我又想,如果我把i让内存回收掉,是不是就会报错了,结果我试了一下并不能回收,可能是因为闭包的关系。其实浏览器里即使是全局函数都是一个闭包,具体为什么我这里理解,大家可以找一找闭包的相关资料看一下。

    那么如何实现我们想要的效果,还是得用到闭包啊,我们需要把当时的i保存下来。代码如下:

    <script>
    var myHref = document.getElementsByTagName("a");
    for (var i = 0,mylength = myHref.length; i<mylength; i++) {
        (function(i){  //这里的i跟外部的i实际不是一个i
            myHref[i].addEventListener("click",function(e){
                e.preventDefault();
                alert(i);
            },"false");
        })(i);
    }
    i=555;    //不会影响
    </script>

    这样写,实际上for循环里是一个立即执行的函数表达式,这种写法()();是立即执行了,立即执行我们的闭包,我们把i当参数传给闭包,闭包里边的i实际作用域只在闭包里,跟外部的i不是一个i,因此就能实现我们想要的效果了。

    总结的不好,见谅。

    -----2015年7月10日,我今天又脑洞大开了一下,为什么会出现这个陷阱。因为click事件(或者所有的DOM事件?反正setTimeout和setInterval也是回调机制处理)是回调机制处理的,由于javaScript是单线程的语言,它会按照javaScript语法出现的次序顺序执行(声明提升实际在这之前发生的),但是遇到click等事件的时候,并不是立刻就执行click对应的函数了了,只是把click事件的函数引用放在一个队列里,继续执行下边的代码,等所有代码执行完毕,就查看任务队列里找第一个,就执行前边存放的click事件的引用,而那时候才进行了所有的赋值,判断等操作,也就是i无法保留的原因。

    按照上边有问题的代码来解释:

    javascript会先执行getElementsByTagName,然后执行for,for里边有一个addEventListener,这个函数也会执行,但是注意,addEventLister的第二个参数,并不是立刻执行了,只是发函数引用放在那了,然后一直到for循环,再然后执行i=555。

    注意:javascript除了主线程,还有一个任务队列的东西,主线程执行完毕了,就去队列找任务,当然我们不点击的话,任务队列就是空的,当我们点击了,addEventLister就会把他的第二个参数的函数放到队列里,然后javaScript主线程突然发现队列里有东西了,赶紧拿出来用吧,但是那时候,大妈已经不是以前的大妈了。。。。。。。

    所以,其实也不是陷阱,是javaScript机制导致的问题。

    阮大侠的博客有介绍,event loop机制的问题。

  • 相关阅读:
    tensorflow mnist数据集 普通神经网络
    cv2 无法读取图片
    tensorflow中的一些函数
    tensorflow中的几种交叉熵
    【BZOJ3489】A simple rmq problem(K-D Tree)
    推门游戏 The Wall Pushers(IDA*)
    [SCOI2007]k短路(A*)
    [SDOI2013]森林(启发式合并)(主席树)
    [模板A*][SCOI2005]骑士精神(A*,IDA*)
    [ZJOI2007]捉迷藏(动态点分治/(括号序列)(线段树))
  • 原文地址:https://www.cnblogs.com/jingubang/p/4632506.html
Copyright © 2020-2023  润新知