javascript中重要概念-闭包-深入理解
在上次的分享中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 值都会覆盖旧值,导致最后输出的是最后一轮的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 只在代码块中有效。也就是说让其成为局部变量。变量 i 是 let 声明的,当前的 i 只在本轮循环有效,所以每一次循环的 i 其实都是一个新的变量,所以最后输出的是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中重要概念-闭包-深入理解的更多相关文章
- JavaScript中call、apply个人理解
JavaScript中call.apply个人理解 一句话即通俗的说:call.apply 是为了改变this的状态而存在的 }; } function personInfo(name,age){ t ...
- JavaScript作用域链与闭包的理解
作用域是JavaScript最重要的概念之一,想要学好JavaScript就需要理解JavaScript作用域和作用域 链的工作原理. 1. 全局作用域(Global Scope) (1)最外层函数和 ...
- javascript 中的new操作符的理解
new 操作符 在有上面的基础概念的介绍之后,在加上new操作符,我们就能完成传统面向对象的class + new的方式创建对象,在Javascript中,我们将这类方式成为Pseudoclassic ...
- 对JavaScript中变量类型的重新理解
<JavaScript启示录>这本书中提出:JavaScript中,对象为“王”(JavaScript里的几乎所有东西都是对象或者用起来像对象). 飞燕草对JavaScript最深刻的理解 ...
- javascript匿名函数及闭包深入理解及应用
1.匿名函数 函数是JavaScript中最灵活的一种对象,这里只是讲解其匿名函数的用途.匿名函数:就是没有函数名的函数. 1.1 函数的定义,首先简单介绍一下函数的定义,大致可分为三种方式 第一种: ...
- javascript中对象字面量的理解
javascript中对象字面量与数组字面量 第一部分 我们知道JavaScript中的数据类型有基本数据类型和引用类型,其中Object类型就是非常常用的类型.那么如果创建一个Object类型的实例 ...
- JavaScript中一些怪异用法的理解
引言 JavaScript这门语言有些场合的用法还是比较怪异的.这篇文章会尽量将这门语言特有的一些比较特殊的用法收集在一起.就当是平时开发时需要注意的地方吧. 特殊用法收集 1.!!用法 在JavaS ...
- JavaScript中的this对象指向理解
在JavaScript中,this不是固定不变的,它的指向取决于上下文环境,一般的,认为this指向使用它时所在的对象.主要有以下几类指向: 在方法中,this 表示该方法所属的对象. 如果单独使用, ...
- JavaScript 中一些概念理解 :clientX、clientY、offsetX、offsetY、screenX、screenY
clientX 设置或获取鼠标指针位置相对于窗口客户区域的 x 坐标,其中客户区域不包括窗口自身的控件和滚动条. clientY 设置或获取鼠标指针位置相对于窗口客户区域的 y 坐标,其中客户区域不包 ...
随机推荐
- JavaScript ES6 的 let 和 var 的比较
在JavaScript 1.7中, let 关键词被添加进来, 我听说它声明之后类似于"本地变量", 但是我仍然不确定它和 关键词 var 的具体区别. 回答: 不同点在于作用域, ...
- Prime Factory
Your task is simple:Find the first two primes above 1 million, whose separate digit sums are also pr ...
- expecting SSH2_MSG_KEX_ECDH_REPLY ssh_dispatch_run_fatal问题解决
设置client的mtu ifconfig eth0 mtu 576 Ultimately, I added the following to /etc/ssh/ssh_config: Host * ...
- Selenium2+python自动化6-八种元素元素定位(Firebug和firepath)
前言 自动化只要掌握四步操作:获取元素,操作元素,获取返回结果,断言(返回结果与期望结果是否一致),最后自动出测试报告.本篇主要讲如何用firefox辅助工具进行元素定位. 元素定位在这四个环节中是至 ...
- static关键字用法
java中static关键字可用于修饰: 1.属性:表示该属性变量在类被加载时即被创建并初始化,类加载过程只进行一次,因此静态变量也只被创建一次 2.方法:静态方法为类的公有方法,可直接用‘类名.方法 ...
- abap常用函数
1.读取生产订单状态函数 call function 'STATUS_READ' exporting client = sy-mandt ...
- [golang]反射的用处--代码自动生成
背景: go语言处理db.json的时候,具体代码的变量定义和db字段.json输出的时候可能不一样. 这个时候,我们需要用tag的方式来进行定义. 例如: type MyStruct struct ...
- 测试C++代码与WebBrowser HTML的互动
testWebBrowserDlg.h // testWebBrowserDlg.h : 头文件 // #pragma once #include "explorer1.h" #i ...
- 数据字典和动态性能视图<五>
数据字典和动态性能视图 介绍:数据字典是什么 数据字典是 oracle 数据库中最重要的组成部分,它提供了数据库的一些系统信息. 动态性能视图记载了例程启动后的相关信息. 数据字典 数据字典记录了 ...
- Daily Scrum 12.10
今日完成任务: 终于修复了大部分的异常,感觉顿时松了一口气.重新在服务器上部署了webservice接口服务以及学霸网站.资源的上传下载功能正在逐渐完善. 遇到困难: 在本地运行正常,部署到服务器上运 ...