zoukankan      html  css  js  c++  java
  • JavaScript中的循环和闭包

    看一段比较经典的错误代码:

    // 希望获取页面上的所有div,在点击的时输出对应的编号
    var oDom = document.querySelectorAll("div");
    
    // 事实上,所有的div被点击输出的都是div的个数加1
    for (var i = 0; i <= oDom.length-1; i++) {
      oDom[i].addEventListener("click", function log() {
        console.log(i+1);
      }, false);
    }
    
    // 希望每秒输出对应的编号,事实上输出的全是6
    for (var i = 1; i <= 5; i++){
      setTimeout(function timer() {
        console.log(i)
      }, i* 1000);
    }
    

    理解上的错误

    这两段代码是JavaScript新手很难理解的地方,为什么就不是我希望的结果?这是曾经让我抓狂不已的东西,但我现在懂了。

    下面,我来分析一下上面两段代码:
    第一段代码中,使用querySelectorAll获取DOM元素,得到的是一个类数组对象NodeList,可以遍历。
    此处使用了for循环遍历,目的是,用addEventListener给每一个div都绑定一个click事件,
    事件处理函数是输出每一个div所在的编号,比如如果是页面上的第一个div,就会输出1,但输出的却是页面上div的数目加1。
    问题其实就在于循环缺陷就是我们假设每一次迭代在运行时都会给自己捕获一个i的副本
    所有的事件绑定函数虽然都是在各自的迭代中定义的,但它们都是被封闭到了共享的全局作用域,因为for循环是没有块级作用域的。
    在共享的全局作用域下,实际上只有1个i。循环结束后,i的值就是div.length+1,因此输出的是div的数目加1。

    第二段代码中,使用了setTimeout延迟函数,目的是每一秒都输出对应的编号,但输出的全是6。
    这里的缺陷是:延迟函数的回调总是会在循环结束才执行。即使setTimeout(..., 0),所有的回调依然是在循环结束后才执行的。
    循环结束后的运行结果跟第一段代码的运行原理一样。

    解决方法

    以上两段话,如果理解了,如何让它变成我们希望的结果呢?
    答案的本质是让for变成一个封闭的块级作用域

    解决方法有两个:
    IIFElet,前者是利用IIFE来创建一个块级作用域,也就是所谓的闭包作用域,后者是利用let的特性会自动转换为块级作用域。
    代码如下:

    // 第一段 IIFE
    for (var i = 0; i <= oDom.length-1; i++) {
      (function(i) {
        oDom[i].addEventListener("click", function log() {
          console.log(i+1);
        }, false);
      })(i)
    }
    // 第一段 let
    for (let i = 0; i <= oDom.length-1; i++) {
      oDom[i].addEventListener("click", function log() {
        console.log(i+1);
      }, false);
    }
    // 第二段 IIFE
    for (var i = 1; i <= 5; i++) {
      (function (i) {
        setTimeout(function timer() {
          console.log(i);
        }, i * 1000);
      })(i);
    }
    // 第二段 let
    for (let i = 1; i <= 5; i++){
      setTimeout(function timer() {
        console.log(i)
      }, i* 1000);
    }
    

    如果有新手再问你这个问题,你可以自豪地跟他说:这个问题曾经让我抓狂,但我现在懂了。然后花一点时间,给他好好整整思路。

    参考

    • 你不知道的JavaScript
  • 相关阅读:
    Leetcode第七题——数的反转
    Leetcode第六题——横向遍历ZIGZAG数组
    26 将查询结果插入到一张表中?
    25 表的复制
    24 insert 语句插入数据
    23 创建表
    22 limit(重点中的重点,以后分页查询全靠它了。)
    21 union(可以将查询结果集相加
    20 子查询
    19 连接查询
  • 原文地址:https://www.cnblogs.com/wljqds/p/11295844.html
Copyright © 2011-2022 走看看