在上次的分享中javascript--函数参数与闭包--详解,对闭包的解释不够深入。本人经过一段时间的学习,对闭包的概念又有了新的理解。于是便把学习的过程整理成文章,一是为了加深自己闭包的理解,二是给读者提供学习的途径,避免走弯路。

以下的分享会分为如下内容:

1.let命令

2.闭包特点的解读

3.循环中的闭包

1.let命令

  在讲闭包前,有必要谈谈ES6中的新概念,let命令。因为在赘述循环中的闭包时会使用到let命令。

  基本用法

  ES6新增了let命令,用来声明变量。它的用法类似于var,但是所声明的变量,只在let命令所在的代码块内有效。

     if (true) {
         var a = 1;
         let b = 2;
     }
     console.log(a);
     console.log(b); // ReferenceError: b is not defined

  在javascript--函数参数与闭包--详解中,谈到在局部变量只能在函数内部声明,在其他代码(如 if 条件语句,for循环语句)用 var 声明的变量都为全局变量。

  在上面代码中,分别用 letvar 声明了两个变量。然后在代码块之外调用这两个变量,结果 let 声明的变量报错,var 声明的变量返回了正确的值。这表明,if 条件语句中使用var声明的变量为全局变量,可以在全局作用域下访问。而 let 声明的变量只在它所在的代码块有效,在全局作用域下无法访问。

  再来看看这两个例子。

     for (let i = 0; i < 10; i++) {}
     console.log(i);  //ReferenceError: i is not defined
     for (var i = 0; i < 10; i++) {}
     console.log(i);  

2.闭包特点的解读

  我们知道,闭包有三个特点

  a:在一个函数内部定义另外一个函数,并返回内部函数或立即执行内部函数。

  b:内部函数可以访问外部函数定义的局部变量 (变量采用var声明)

  c:让局部变量始终保存在内存中。也就是说,闭包可以使得它诞生的环境一直存在。

  我们来看一个例子,尝试串起这三个特点。

     function keith() {
         var a = 1;
         return function() {
             return a++;
         }
     }
     var result = keith();
     console.log(result());
     console.log(result());
     console.log(result()); 

  首先,在函数keith内部返回了一个匿名函数,如果函数keith没有返回值,则默认返回值为undefined。

  然后,因为在函数keith中返回了一个匿名函数,又把调用函数keith的结果赋值给了全局变量result,所以全局变量result是一个闭包。当连续调用result时,依次返回1,2,3。返回值说明了内部函数可以访问外部函数定义的局部变量。也就是说,闭包记住了外部函数定义的局部变量的调用结果。

  最后,因为我们把一个闭包赋值给了一个全局变量result,在调用时依次输出1,2,3。说明了在函数keith外部访问的这个局部变量a一直存在全局作用域中。也就是说,局部变量 a 一直存在于内存当中,所以不会被垃圾回收机制回收。

  所以使用闭包的时候的注意点:

  由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄露。解决方法是,在退出函数之前,将不使用的局部变量全部删除。

3.循环中的闭包

  一个常见的错误出现在循环中使用闭包,假设我们需要在每次循环中调用循环序号。

     for (var i = 0; i < 10; i++) {
         setTimeout(function() {
             console.log(i);
         }, 1000)
     }

  上面代码中,不会符合我们的预期,输出数字0-9。而是会输出数字10十次。

  出现错误的原因在于我们在setTimeout函数里面定义了一个匿名函数,匿名函数的作用是在控制台输出变量 i,而变量 i 是一个全局变量,在全局范围内都有效。所以每一次循环,新的 i 值都会覆盖旧值,导致最后输出的是最后一轮的i的值。

  所以,针对循环中的闭包,有以下两种解决方法。

  一是使用立即执行函数(IIFE),并把 i 作为它的参数,此时函数内 e 变量就拥有了 i 的一个拷贝。当传递给 setTimeout 的匿名函数执行时,它就拥有了对 e 的引用,而这个值是不会被循环改变的。

     for (var i = 0; i < 10; i++) {
         (function(e){
             setTimeout(function() {
                 console.log(e); //1,2,3,....,10
             }, 1000)
         })(i)
     }

  二是让变量 i 只在代码块中有效。也就是说让其成为局部变量。变量 i let 声明的,当前的 i 只在本轮循环有效,所以每一次循环的 i 其实都是一个新的变量,所以最后输出的是1,2,3,4....,10。

     for (let i = 0; i < 10; i++) {
         setTimeout(function() {
             console.log(i); //1,2,3...,10
         }, 1000)
     }

  

完。

感谢大家的阅读。

转载请注明出处:http://www.cnblogs.com/Uncle-Keith/p/5801015.html

深入理解javascript闭包(二)的更多相关文章

  1. 我从来不理解JavaScript闭包,直到有人这样向我解释它...

    摘要: 理解JS闭包. 原文:我从来不理解JavaScript闭包,直到有人这样向我解释它... 作者:前端小智 Fundebug经授权转载,版权归原作者所有. 正如标题所述,JavaScript闭包 ...

  2. 【转】深入理解JavaScript闭包闭包(closure) (closure)

    一.什么是闭包?"官方"的解释是:闭包是一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数),因而这些变量也是该表达式的一部分.相信很少有人能直接看懂这句话,因为他描述 ...

  3. 全面理解Javascript闭包和闭包的几种写法及用途

    好久没有写博客了,过了一个十一长假都变懒了,今天总算是恢复状态了.好了,进入正题,今天来说一说javascript里面的闭包吧!本篇博客主要讲一些实用的东西,主要将闭包的写法.用法和用途.  一.什么 ...

  4. 深入理解JavaScript闭包(closure)

    最近在网上查阅了不少javascript闭包(closure)相关的资料,写的大多是非常的学术和专业.对于初学者来说别说理解闭包了,就连文字叙述都很难看懂.撰写此文的目的就是用最通俗的文字揭开Java ...

  5. 深入理解javascript闭包(一)

    闭包(closure)是Javascript语言的一个难点.也是它的特色,非常多高级应用都要依靠闭包实现. 一.什么是闭包? 官方"的解释是:闭包是一个拥有很多变量和绑定了这些变量的环境的表 ...

  6. 深入理解javascript闭包(一)

    原文转自脚本之家(http://www.jb51.net/article/24101.htm) 闭包(closure)是Javascript语言的一个难点,也是它的特色,很多高级应用都要依靠闭包实现. ...

  7. 深入理解Javascript闭包概念

    一.变量的作用域 要理解闭包,首先必须理解Javascript特殊的变量作用域. 变量的作用域无非就是两种:全局变量和局部变量. Javascript语言的特殊之处,就在于函数内部能够直接读取全局变量 ...

  8. 轻松理解JavaScript闭包

    摘要 闭包机制是JavaScript的重点和难点,本文希望能帮助大家轻松的学习闭包 一.什么是闭包? 闭包就是可以访问另一个函数作用域中变量的函数. 下面列举出常见的闭包实现方式,以例子讲解闭包概念 ...

  9. 【译】理解JavaScript闭包——新手指南

    闭包是JavaScript中一个基本的概念,每个JavaScript开发者都应该知道和理解的.然而,很多新手JavaScript开发者对这个概念还是很困惑的. 正确理解闭包可以帮助你写出更好.更高效. ...

  10. 转 全面理解Javascript闭包和闭包的几种写法及用途

    转自:http://www.cnblogs.com/yunfeifei/p/4019504.html 好久没有写博客了,过了一个十一长假都变懒了,今天总算是恢复状态了.好了,进入正题,今天来说一说ja ...

随机推荐

  1. 如何在Open Live Writer(OLW)中使用precode代码高亮Syntax Highlighter

    早先Microsotf的Windows Live Writer(WLW)现在已经开源了,并且更名为Open Live Writer,但是现在Windows Live Writer还是可以现在,Open ...

  2. Mono for Android—初体验之“电话拨号器”

    1.Main.axml文件: <?xml version="1.0" encoding="utf-8"?><LinearLayout xmln ...

  3. 一步步学习javascript基础篇(0):开篇索引

    索引: 一步步学习javascript基础篇(1):基本概念 一步步学习javascript基础篇(2):作用域和作用域链 一步步学习javascript基础篇(3):Object.Function等 ...

  4. React单元测试——十八般兵器齐上阵,环境构建篇

    一个完整.优秀的项目往往离不开单元测试的环节,就 github 上的主流前端项目而言,基本都有相应的单元测试模块. 就 React 的项目来说,一套完整的单元测试能在在后续迭代更新中回归错误时候给与警 ...

  5. Ubuntu部署python3.5的开发和运行环境

    Ubuntu部署python3.5的开发和运行环境 1 概述 由于最近项目全部由python2.x转向 python3.x(使用目前最新的 python3.5.1) ,之前的云主机的的默认python ...

  6. CSharpGL(32)矩阵与四元数与角度旋转轴的相互转换

    CSharpGL(32)矩阵与四元数与角度旋转轴的相互转换 三维世界里的旋转(rotate),可以用一个3x3的矩阵描述:可以用(旋转角度float+旋转轴vec3)描述.数学家欧拉证明了这两种形式可 ...

  7. [译]ZOOKEEPER RECIPES-Queues

    队列 分布式队列是一种常见的数据结构.为了在ZooKepeer中实现分布式队列,第一步是要使用一个znode代表队列本身.分布式客户端通过create()方法将内容放入一个名叫"queue- ...

  8. ASP.NET MVC5+EF6+EasyUI 后台管理系统(57)-插件---ueditor使用

    系列目录 目录: 前言 开发环境 知识点 初始使用 自定义工具栏 设置和读取编辑器内容 文件上传 ueditor加水印 ---------------------------------------- ...

  9. 从零开始编写自己的C#框架(12)——T4模板在逻辑层中的应用(一)(附源码)

    对于T4模板很多朋友都不太熟悉,它在项目开发中,会帮我们减轻很大的工作量,提升我们的开发效率,减少出错概率.所以学好T4模板的应用,对于开发人员来说是非常重要的. 园子里对于T4模板的介绍与资料已经太 ...

  10. YYModel 源码解读(二)之YYClassInfo.h (1)

    NS_ASSUME_NONNULL_BEGIN NS_ASSUME_NONNULL_END 为了兼容Swift 中的 ? 和 ! oc 在6.3引入了两个新的类型注释:__nullable和__non ...