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

在javascript--函数参数与闭包--详解这篇文章中,我详细介绍了闭包的概念。以下的分享对闭包的基本概念只会稍稍带过。如果对闭包的概念不熟悉的同学,请移步至javascript--函数参数与闭包--详解。

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

1.let命令

2.闭包特点的解读

3.循环中的闭包

1.let命令

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

  基本用法

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

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

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

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

  再来看看这两个例子。

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

1     for (var i = 0; i < 10; i++) {}
2     console.log(i);  // 10

2.闭包特点的解读

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

  a:在一个函数内部定义另外一个函数。

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

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

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

 1     function keith() {2         var a = 1;3         return function() {4             return a++;5         }6     }7     var result = keith();8     console.log(result()); //19     console.log(result()); //2
10     console.log(result()); //3

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

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

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

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

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

3.循环中的闭包

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

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

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

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

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

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

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

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

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

  

javascript中重要概念-闭包-深入理解相关推荐

  1. 12.在JavaScript中的事件模型如何理解?

    一.事件与事件流 javascript中的事件,可以理解就是在HTML文档或者浏览器中发生的一种交互操作,使得网页具备互动性, 常见的有加载事件.鼠标事件.自定义事件等 由于DOM是一个树结构,如果在 ...

  2. javascript中浅拷贝和深拷贝的理解

    javascript中浅拷贝和深拷贝的理解 什么是拷贝? 简单地说就是复制,对数据的复制 浅拷贝:改变拷贝者的值,被拷贝者的值也会变化 深拷贝:改变拷贝者的值,被拷贝者的值不会变化 由于基本数据类型是 ...

  3. JavaScript中call、apply个人理解

    JavaScript中call.apply个人理解 一句话即通俗的说:call.apply 是为了改变this的状态而存在的 var lisi = {name:'李四',age:23};var zs ...

  4. JavaScript中的作用域,闭包和上下文

    深入理解JavaScript中的作用域和上下文 很多语言当中都会有作用域的概念,它会给我们带来便利,偶尔也会有烦恼,只有清楚地理解和掌握了它,才能更好地为我所用,今天就带来这么一篇文章供大家参考. 介 ...

  5. javascript 中的暗物质 - 闭包

    1. 诡异的闭包 javascript 中有一个特殊的特性 - 闭包,对于 .NET 程序员来说,比较熟悉的是面向对象的程序设计 OOP,  而来自函数式语言的闭包则显得比较诡异,许多程序员对它敬而远 ...

  6. javascript匿名函数及闭包深入理解及应用

    1.匿名函数 函数是JavaScript中最灵活的一种对象,这里只是讲解其匿名函数的用途.匿名函数:就是没有函数名的函数. 1.1 函数的定义,首先简单介绍一下函数的定义,大致可分为三种方式 第一种: ...

  7. 对javascript中的匿名函数的理解

    (function(){//这里的所有变量和函数都属于局部对象 }()); 在javascript中以function开头的语句通常是函数声明.加上了外面的括号(黄色背景)后则创建的是函数表达式. 蓝 ...

  8. JavaScript中加号运算符+ 运算过程理解

    在JavaScript中二元加法运算符"+"可以对两个数字或者字符串进行连接操作. 1+2=>3 "hello"+" "+" ...

  9. javascript中 __proto__与prorotype的理解

    我们先看看这样一段代码: 1 <script type="text/javascript"> 2 var Person = function () { }; 3 var ...

最新文章

  1. 植物MWAS研究—小米产量与微生物组关联分析
  2. go dll 传char*
  3. ITK:将itk :: Image转换为vtkImageData
  4. 2pc_two phase commit详情
  5. 第一次正经面试之发现自己的缺陷和不足
  6. cam350 不能打开光绘文件_如何在CAM350中导入Allegro光绘
  7. 13--长度最小的子数组
  8. c语言辗转相除法求最大公约数_趣味探究:妙法求“最大公因数”,比书上难一点,你敢挑战吗?(适合56年级)...
  9. 【Android】11.3 屏幕旋转和场景变换过程中GridView的呈现
  10. 诺禾致源css客户端,诺禾,诺禾致源:CSS 基础教学
  11. Android.mk宏定义demo
  12. 游戏开发之多态及虚函数(C++基础)
  13. Focal Loss 和 LightGBM 多分类应用-python实现
  14. Sublime安装使用插件pretty json
  15. php 支付宝用户信息授权,支付宝登录获取用户信息授权
  16. 微信小程序注册流程详解
  17. 手机参数中的4+64G到底是什么?
  18. 零基础新手如何学习SEO
  19. Git基础之(三)——时光穿梭机
  20. mysql之聚簇索引与非聚簇索引

热门文章

  1. 【Smart_Point】unique_ptr中独占指针使用MakeFrame
  2. gdb+gdbserver
  3. ubuntu设置securecrt串口权限
  4. 【CSDN2012年度博客之星】需要您的一票,感谢大家的支持
  5. apache的keepalive和keepalivetimeout(apache优化)
  6. 用ext_skel,实现一个PHP扩展,添加到PHP并调用
  7. C++中struct的使用
  8. libcurl库的使用(通过libcurl库下载url图像)
  9. 图片像素、英寸、厘米之间的单位换算
  10. 图像空间变换--imtransform