循环与闭包

    先看一个demo

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<script>
for(var i = 0; i <= 5; i++){ setTimeout(function timer(){
console.log(i);
}, i*1000); } </script>
</body>
</html>

    结果会打印出什么? 相信大家都知道 答案是 6个6; 我们本意是打印出:0,1,2,3,4,5  但结果却是6个6, 为什么?首先解释一下为什么打印出6:这里涉及到延时函数的执行机制,虽然延时函数表明是i秒后执行,而i秒是相对与所有可执行代码执行完那一刻开始计时的,也就是等for循环结束之后,延时函数才开始执行。但此时 i= 6;所以会打印出6. 现在在解释一下为什么是6个6:很简单,for循环每执行一次就创建出一个延时函数,执行了6次,就会有6个延时函数。这6个延时函数共享一个作用域(本例中全局作用域) i又累加到6,所以会打印出6个6!!

    分析完整个过程,你知道问题出现在哪里吗?   有2个问题  1:每执行一次for循环,应该执行一次延时函数,但并没有。2:所有的延时函数共享一个作用域。

知道问题的地方,那就很好解决了:每循环一次,创建一个作用域 。 关键是如何实现“每循环一次,创建一个作用域”, 其实很简单,每循环一次,就立即执行一次延时函数,立即执行函数(IIFE)就派上用场了 它的原理很“粗暴”:每个延时函数都会将IIFE在每次迭代中创建的作用域 封闭起来。ok,修改一下我们的代码:

  

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<script>
for(var i = 0; i <= 5; i++){
(function(){
setTimeout(function timer(){
console.log(i);
}, i*1000);
})(); } </script>
</body>
</html>

  在chrome中显示:

  咦??? 怎么还是6个6!!!  问题出在哪里?  其实很简单,仔细看一下,我们的IIFE只是一个什么都没有的空作用域(i是全局变量,处于全局作用域中) 它需要有自己的变量,用来在每个迭代中存储i的值   再来改进一下代码:

  

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<script>
for(var i = 0; i <= 5; i++){
(function(){
var j = i;
setTimeout(function timer(){
console.log(j);
}, j*1000);
})(); } </script>
</body>
</html>

  在chrome中显示:

  把i值存进来,就可以实现 我们的预期了。到现在为止,代码已经是没有错误的了。但是,还可以优化:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<script>
for(var i = 0; i <= 5; i++){
(function(i){
setTimeout(function timer(){
console.log(i);
}, i*1000); })(i)
} </script>
</body>
</html>

把i当作参数传进去,我们的代码就更完美了。

                                        2017-3-23  0:19

JavaScript---循环与闭包的更多相关文章

  1. JavaScript葵花宝典之闭包

    闭包,写过JS脚本的人对这个词一定不陌生,都说闭包是JS中最奇幻的一个知识点,  虽然在工作中,项目里经常都会用到~  但是是不是你已经真正的对它足够的了解~~ 又或者是你代码中出现的闭包,并不是你刻 ...

  2. 让你分分钟学会Javascript中的闭包

    Javascript中的闭包 前面的话: 闭包,是 javascript 中重要的一个概念,对于初学者来讲,闭包是一个特别抽象的概念,特别是ECMA规范给的定义,如果没有实战经验,你很难从定义去理解它 ...

  3. 深入理解javascript原型和闭包(4)——隐式原型

    注意:本文不是javascript基础教程,如果你没有接触过原型的基本知识,应该先去了解一下,推荐看<javascript高级程序设计(第三版)>第6章:面向对象的程序设计. 上节已经提到 ...

  4. javascript中的闭包、模块与模块加载

    一.前言 闭包是基于词法作用域(  和动态作用域对应,词法作用域是由你写代码时,将变量写在哪里来决定的,因此当词法分析器处理代码时,会保持作用)书写代码时所产生的自然结果,甚至不需要为了利用闭包而有意 ...

  5. 浅谈JavaScript中的闭包

    浅谈JavaScript中的闭包 在JavaScript中,闭包是指这样一个函数:它有权访问另一个函数作用域中的变量. 创建一个闭包的常用的方式:在一个函数内部创建另一个函数. 比如: functio ...

  6. javascript中的闭包解析

    学习javaScript已经有一段时间了,在这段时间里,已经感受到了JavaScript的种种魅力,这是一门神奇的语言,同时也是一门正在逐步完善的语言,相信在大家的逐步修改中,这门语言会逐步的完善下去 ...

  7. 《深入理解javascript原型和闭包系列》 知识点整理(转)

    深入理解javascript原型和闭包系列 对原型和闭包等相关知识的讲解,由浅入深,通俗易懂,每个字都值得细细研究. 一.一切都是对象 1. typeof操作符输出6种类型:string boolea ...

  8. 《深入理解javascript原型和闭包系列》 知识点整理

    深入理解javascript原型和闭包系列 对原型和闭包等相关知识的讲解,由浅入深,通俗易懂,每个字都值得细细研究. 一.一切都是对象 1. typeof操作符输出6种类型:string boolea ...

  9. JavaScript中的闭包理解

    原创文章,转载请注明:JavaScript中的闭包理解  By Lucio.Yang 1.JavaScript闭包 在小学期开发项目的时候,用node.js开发了服务器,过程中遇到了node.js的第 ...

  10. javascript作用域和闭包之我见

    javascript作用域和闭包之我见 看了<你不知道的JavaScript(上卷)>的第一部分--作用域和闭包,感受颇深,遂写一篇读书笔记加深印象.路过的大牛欢迎指点,对这方面不懂的同学 ...

随机推荐

  1. 使用LocationManager来获取移动设备所在的地理位置信息

    在Android应用程序中,可以使用LocationManager来获取移动设备所在的地理位置信息.看如下实例:新建android应用程序TestLocation. 1.activity_main.x ...

  2. 一步一步学EF系列三【数据迁移】

    我们每篇的内容都不多,所以希望在学习的过程中最后能亲自敲一下代码 这样更有利于掌握. 我们现在接着上篇的例子,我们现在给随便的表增加一个字段 CreateTime 创建日期 运行一下 看看会怎么样 修 ...

  3. PKU 1521 Entropy(简单哈弗曼树_水过)

    题目大意:原题链接 给你一个字符串,首先是计算出一个按正常编码的编码长度,其次是计算出一个用霍夫曼编码的编码长度,最后求正常编码的长度除以霍夫曼编码长度的比值,保留一位小数. 解题思路:需要知道 1. ...

  4. 使用Xib创建自定义视图(不是cell)时需要注意的问题

    开发项目过程中,有些地方不免会用到Xib来提高开发效率,如果你的手速够快,写代码建视图,我并不反对这样做.因为我以前也是纯手写代码开发. 进入正题,Xib好用,但是这些下面这些问题需要注意一下. 问题 ...

  5. oracle安装完成后目录中不论有没有tnsnames.ora和listener.ora文件 PLSQL都能连上的问题解决方法

    今天遇到这个问题了,发现listener.ora文件和tnsnames.ora文件在Net Work文件夹下没有,正常情况下安装完oracle或者是oracle Client是会有的,但是在Net M ...

  6. Linux上db2安装

    Linux上db2安装:https://blog.csdn.net/nayanminxing/article/details/69372283

  7. 【c++ primer, 5e】构造函数 & 拷贝、赋值和析构

    [构造函数] 1.构造器就是创建对象时被调用的代码. 2.如果没有自定义构造器,那么编译器将自动合成一个默认的无参构造器. 3.自定义的构造器不允许加const,所创建const的对象只有在构造器代码 ...

  8. LeetCode (65):Same tree

    Total Accepted: 83663 Total Submissions: 200541 Difficulty: Easy Given two binary trees, write a fun ...

  9. C++之STL迭代器(iterator)

    [摘要]本文是对STL--迭代器(iterator)的讲解,对学习C++编程技术有所帮助,与大家分享. 原文:http://www.cnblogs.com/qunews/p/3761405.html ...

  10. bat(续六)-windows批处理set命令

    windows批处理set命令 [设置变量]格式:set 变量名=变量值详细:被设定的变量以%变量名%引用 [取消变量]格式:set 变量名=详细:取消后的变量若被引用%变量名%将为空 [展示变量]格 ...