在上次的分享中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()); //1
9 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. JavaScript中call、apply个人理解

    JavaScript中call.apply个人理解 一句话即通俗的说:call.apply 是为了改变this的状态而存在的 }; } function personInfo(name,age){ t ...

  2. JavaScript作用域链与闭包的理解

    作用域是JavaScript最重要的概念之一,想要学好JavaScript就需要理解JavaScript作用域和作用域 链的工作原理. 1. 全局作用域(Global Scope) (1)最外层函数和 ...

  3. javascript 中的new操作符的理解

    new 操作符 在有上面的基础概念的介绍之后,在加上new操作符,我们就能完成传统面向对象的class + new的方式创建对象,在Javascript中,我们将这类方式成为Pseudoclassic ...

  4. 对JavaScript中变量类型的重新理解

    <JavaScript启示录>这本书中提出:JavaScript中,对象为“王”(JavaScript里的几乎所有东西都是对象或者用起来像对象). 飞燕草对JavaScript最深刻的理解 ...

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

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

  6. javascript中对象字面量的理解

    javascript中对象字面量与数组字面量 第一部分 我们知道JavaScript中的数据类型有基本数据类型和引用类型,其中Object类型就是非常常用的类型.那么如果创建一个Object类型的实例 ...

  7. JavaScript中一些怪异用法的理解

    引言 JavaScript这门语言有些场合的用法还是比较怪异的.这篇文章会尽量将这门语言特有的一些比较特殊的用法收集在一起.就当是平时开发时需要注意的地方吧. 特殊用法收集 1.!!用法 在JavaS ...

  8. JavaScript中的this对象指向理解

    在JavaScript中,this不是固定不变的,它的指向取决于上下文环境,一般的,认为this指向使用它时所在的对象.主要有以下几类指向: 在方法中,this 表示该方法所属的对象. 如果单独使用, ...

  9. JavaScript 中一些概念理解 :clientX、clientY、offsetX、offsetY、screenX、screenY

    clientX 设置或获取鼠标指针位置相对于窗口客户区域的 x 坐标,其中客户区域不包括窗口自身的控件和滚动条. clientY 设置或获取鼠标指针位置相对于窗口客户区域的 y 坐标,其中客户区域不包 ...

随机推荐

  1. rutime中动态调用类的方法

    Dynamically invoke a class method in Objective C 代码 #import <Foundation/Foundation.h> #import ...

  2. Docx读写Word

    Docx.dll功能比较强大,具备以下功能: 创建新的word文档或者读取已有的world文档 替换书签处内容: 插入表格或者在已有表格新增数据行: 插入图片,轻松设置图片大小: 保存或者另存为: 分 ...

  3. RHEL7网络管理之nmcli

    在RHEL7中默认使用NetworkManager 守护进程来监控和管理网络设置.nmcli是命令行的管理NetworkManager的工具,会自动把配置写到/etc/sysconfig/networ ...

  4. HDOJ 1512 几乎模板的左偏树

    题目大意:有n个猴子.每个猴子有一个力量值,力量值越大表示这个猴子打架越厉害.如果2个猴子不认识,他们就会找他们认识的猴子中力量最大的出来单挑,单挑不论输赢,单挑的2个猴子力量值减半,这2拨猴子就都认 ...

  5. ios 项目引用全局pch文件

    1.在项目中新建添加PCH文件 把这些记下来,下次直接粘贴:$(SRCROOT)/工程名/pch文件名

  6. Html模板

    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 5.0 Transitional//EN"> <!-- saved from ur ...

  7. mui学习笔记

    一.页面刷新问题 1.父页面A跳转到子页面B,B页面修改数据后再跳回A页面,刷新A页面数据 (1).父页面A代码 window.addEventListener("pageflowrefre ...

  8. 0909 45作业one

    1.编译原理学什么? 答: 初遇编译原理,我知道编译原理是计算机专业设置的一门重要的专业课程,主要是介绍编译程序构造的一般原理和基本方法.其内容大概包括语言和文法.词法分析.语法分析.语法制导翻译.中 ...

  9. 关于collapsed margin(外边距合并)

    这是前面写postion定位时写到最后面的例子的时候发现的一个问题,于是专门写一篇随笔来解释记录一下,毕竟两个知识点同时写在一篇文章里面有点混乱的感觉.. 上篇随笔position定位遇到的问题在这里 ...

  10. C++Builder 中 Enter键或者Tab键无效

    VC++中或者C++Builder中 当回车键不能换行时应该就是因为自己不小心按到了键盘上的insert键,要想恢复只需要再按一下就可以了. 小经验