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变成一个封闭的块级作用域。
解决方法有两个:
IIFE和let,前者是利用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
 
JavaScript中的循环和闭包的更多相关文章
- 前端学习 第六弹:  javascript中的函数与闭包
		
前端学习 第六弹: javascript中的函数与闭包 当function里嵌套function时,内部的function可以访问外部function里的变量 function foo(x) { ...
 - JavaScript中For循环以及For循环嵌套实例
		
JavaScript中For循环实例 1.打印出所有的 "水仙花数 ",所谓 "水仙花数 "是指一个三位数,其各位数字立方和等于该数本身. 例如:153是一个 ...
 - Javascript中的循环变量声明,到底应该放在哪儿?
		
相信很多Javascript开发者都在声明循环变量时犹豫过var i到底应该放在哪里:放在不同的位置会对程序的运行产生怎样的影响?哪一种方式符合Javascript的语言规范?哪一种方式和ecma标准 ...
 - javascript中的原型和闭包
		
定义 //闭包测试 function bbTest() { var local = "这里是本地变量"; //闭包会扩大局部变量的作用域,具备变量一致会存活到函数之外,在函数之外可 ...
 - [译]Javascript中的循环
		
本文翻译youtube上的up主kudvenkat的javascript tutorial播放单 源地址在此: https://www.youtube.com/watch?v=PMsVM7rjupU& ...
 - JavaScript 中 for 循环
		
在ECMAScript5(简称 ES5)中,有三种 for 循环,分别是: 简单for循环 for-in forEach 在2015年6月份发布的ECMAScript6(简称 ES6)中,新增了一种循 ...
 - JavaScript基础Javascript中的循环(003)
		
1.普通循环JavaScript中一般的循环写法是这样的: // sub-optimal loop for (var i = 0; i < myarray.length; i++) { // d ...
 - JavaScript中的作用域和闭包
		
首先强烈安利<你不知道的JavaScript>,JS初学者进阶必读. 对于从C++.Java等静态语言转向JavaScript的初学者(比如我)来说,JS一些与众不同而又十分要紧的特性使得 ...
 - JavaScript中的函数:闭包,this,高阶函数
		
一.函数基本理论 function compare(val1,val2){ return val1 - val2; }var result = compare(5,10); 1,函数的定义没什么意义, ...
 
随机推荐
- 一个proc预编译代码时coredump的问题分析
			
最近有同事在搞编译环境迁移,碰上一个问题让我帮他看一下. 他建了一个新目录,然后把现在的代码拷过去,编译的时候发现有一个文件编译不了一执行就出现core,不知道啥情况. 我进到他的编译环境 ...
 - wx-icon和progress
			
基本内容 index.wxml <!--index.wxml--> <view class="container"> <!--icon text pr ...
 - Android 插件化开发(三):资源插件化
			
在前面的文章中我们成功的加载了外部的Dex(Apk)并执行了插件的Bean代码.这时我们会想,能不能加载并运行插件Apk的Activity.答案当然是能,否则后续我们的研究就没意义了,但是想实现Act ...
 - 蒟蒻的PKUWC2019划水记(更新ing)
			
前言 (结束再补) \(Dec\ 20th\) 正式出发 今天,是正式出发的日子. 虽说是星期五,可并没有去学校晨跑.难得睡到了\(7\)点,起来匆匆吃完了早饭(一个手抓饼),就出发去火车站了. 到了 ...
 - nginx和swoole怎么混合使用
			
有需要学习交流的友人请加入交流群的咱们一起,有问题一起交流,一起进步!前提是你是学技术的.感谢阅读! 点此加入该群jq.qq.com 基于epoll的Nginx 有了epoll,理论上1个进程就可以 ...
 - 计算几何 val.1
			
目录 计算几何 val.1 向量的点积 向量的叉积 一种奇怪的三角剖分求面积 凸包 点绕点旋转 后记 计算几何 val.1 本文并不是入门文章,供有高中数学基础的阅读 主要写一些重要的点和注意事项吧 ...
 - 使用EasyPOI导入导出图片出现数组越界异常
			
在我使用easypoi做导出功能的时候,突然抛了一个数组越界异常,找了很久也没找到,最后猜想有可能是路径出了问题,然后打印了一下图片存放的路径,结果发现在其保存路径上存在“.”,也就是easypoi底 ...
 - python实现智能语音天气预报
			
前言 本文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理. 作者: 飞奔的帅帅 PS:如有需要Python学习资料的小伙伴可以加点击下 ...
 - SpringCloud服务配置中心
			
SpringCloud Config简介 Spring Cloud Config 是 Spring Cloud 团队创建的一个全新项目,用来为分布式系统中的基础设施和微服务应用提供集中化的外部配置支持 ...
 - SSH框架之Spring第二篇
			
1.1 基于注解的IOC配置 既注解配置和xml配置要实现的功能都是一样的,都是要降低程序间的耦合.只是配置的形式不一样. 1.2 环境搭建 1.2.1 第一步:拷贝必备的jar包 需要多拷贝一个sp ...