javascript闭包—围观大神如何解释闭包

闭包的概念已经出来很长时间了,网上资源一大把,本着拿来主意的方法来看看。

这一篇文章 学习Javascript闭包(Closure) 是大神阮一峰的博文,作者循序渐进,讲的很透彻。下面一一剖析。

1.变量的作用域

变量的作用域有局部和全局两种,在javascript的函数内部可以访问全局变量,如下:

  // 函数内部可以直接读取全局变量var n = 99;function f1() {alert(n);}f1();

在f1函数中可以访问到全局变量n。输出如下:

反过来就不行了,在函数外部不能读取函数内部的变量,例如这样:

  //函数外部无法读取函数内部的局部变量,这里会报错function f2() {var m = 99;}alert(m) //报错

javascript还有一个比较特殊的地方,在函数内部如果没有使用var,const,let修饰符声明变量,那么这个变量不再是局部变量而是一个全局变量,如下:

  function f2() {m = 99;}f2();alert(m)

输出99:

是不是很神奇,但是这个经常给人造成困惑。

 

2.如何从外部读取函数内部的局部变量

很多场合下要访问函数内部的局部变量,变通的方式是在函数内部定义函数,如下:

  function f3() {var a = 999;function f4() {alert(a);}return f4}var result = f3();result();

函数f4包含在函数f3里面,所以f4范围内可以访问到f3中的那个变量a,反过来是不行的,Javascript语言特有的"链式作用域"结构(chain scope),子对象会一级一级地向上寻找所有父对象的变量。所以,父对象的所有变量,对子对象都是可见的,反之则不成立。

 

3.闭包的概念

可以简单理解成定义在函数内部的函数,这个内部函数可以把内部函数作用域内的变量传播到外面。由于在javascript中只有内部的子函数才能读取局部变量,因此可以把闭包简单的理解成“定义在一个函数内部的函数”。本质上,闭包就是将函数内部和函数外部连接起来的桥梁。

 

4.闭包的作用

闭包的第一个用处就是读取函数内部的变量,另一个作用就是让这些变量始终保存在内存中。来看下面的代码:

  function f5() {var b = 111;nAdd = function () {b += 1;}function f6() {alert(b);}return f6}var result1 = f5();result1();nAdd();result1();

上面代码两次弹出框,第一次是输出111,第二次是112,这就证明函数函数f5内的局部变量b一直保存在内存中,并没有在f5调用后被自动清除。

这就说明,第一次调用result1();的时候给变量b赋值了,然后调用全局函数nAdd的时候变量b仍然还在内存中,给他加1就变成112了。原因就在于f5是f6的父函数,而f6被赋给了一个全局变量,这导致f6始终在内存中,而f6的存在依赖于f5,因此f5也始终在内存中,不会在调用结束后,被垃圾回收机制(garbage collection)回收。

这段代码中另一个值得注意的地方,就是"nAdd=function(){n+=1}"这一行,首先在nAdd前面没有使用var关键字,因此nAdd是一个全局变量,而不是局部变量。其次,nAdd的值是一个匿名函数(anonymous function),而这个匿名函数本身也是一个闭包,所以nAdd相当于是一个setter,可以在函数外部对函数内部的局部变量进行操作。

5.注意 

1)由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄露。解决方法是,在退出函数之前,将不使用的局部变量全部删除。其实一般不会这样用!!!
2)闭包会在父函数外部,改变父函数内部变量的值。所以,如果你把父函数当作对象(object)使用,把闭包当作它的公用方法(Public Method),把内部变量当作它的私有属性(private value),这时一定要小心,不要随便改变父函数内部变量的值。其实一般不会这样用!!!

最后作者给出了思考题目,如下:

  var name = 'The Window';var object = {name: 'My Object',getNameFunc: function () {// 这里的this是函数console.info(this)return function () {//匿名函数的执行环境是windowsconsole.info(this)return this.name;}}};alert(object.getNameFunc()());

其实从变量的值已经看到答案了,在对象object内部的函数getNameFunc里面返回了一个匿名函数这个匿名函数的作用域是window,所以这里输出的是'The Window',如下:

我在日志里面加上的调试语句可以看出端倪:

看下面的代码:

  var name1 = "The Window";var object1 = {name1: 'My Object1',getNameFunc:function () {var that = this;return function () {return that.name1}}}alert(object1.getNameFunc()());

这里在函数内部使用var that = this语句先把当前上下文的对象保存下来,在匿名函数中使用that.name1,这样就是当前对象中的name1,于是输出了“MyObject1”。其实可以用es6中的箭头函数,如下:

  var name1 = "The Window";var object1 = {name1: 'My Object1',getNameFunc:function () {return  () => {return this.name1}}}alert(object1.getNameFunc()());

箭头函数会绑定object1的作用域,于是仍任是object1的属性name1,和上面输出的结果一样。

6.深入的理解

 在知乎上也有人在讨论这个问题,知乎你懂的,比较严谨,如何通俗易懂的解释javascript里面的‘闭包’?这一篇问答里有人给出了其他的解释。

 1.每次定义一个函数,都会产生一个作用域链(scope chain)。当JavaScript寻找变量varible时(这个过程称为变量解析),总会优先在当前作用域链的第一个对象中查找属性varible ,如果找到,则直接使用这个属性;否则,继续查找下一个对象的是否存在这个属性;这个过程会持续直至找到这个属性或者最终未找到引发错误为止。
------有道理,关键是弄懂这个作用域链,说白了就是花括号的层级及各种函数的的上下文作用域,比如在函数中定义变量不用var,let它居然是全局的,这个是javascript比较特殊的地方,强类型语言估计早就报错了。

2.JavaScript中的函数运行在它们被定义的作用域里,而不是它们被执行的作用域里。
------有道理,上面的最后一个代码段中的例子,本来执行object1.getNameFunc()()这一句的时候,执行作用域中的name1是var name1 = "The Window";这个,但是弹出来的确实定义getNameFunc这个函数的作用域内的name1: 'My Object1',

3.子函数能够访问父函数的局部变量,反之则不行。而那个子函数就是闭包!
------有道理,就是上面阮大师反复说明的

好了,就这么多了。

 

转载于:https://www.cnblogs.com/tylerdonet/p/6561334.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/284247.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

.NET 20周年专访 - 张善友:.NET 技术是如何赋能并改变世界的

点击蓝字关注我们今年是 .NET 发布20周年,值此20周年之际,微软 Reactor 特别策划了 .NET 20周年系列主题专访。我们邀请了数位中国 .NET 领域的技术专家以及社区名人,来聊聊他们与 .NET 的情缘、认识 .NET 的契机、选择 .NET 的理由&#xff…

【ArcGIS错误异常100问】之005:ArcGIS字段计算器python中文编码问题解决

问题描述: 现因工作的需要,对照2017最新版:《土地利用现状分类》(GBT 21010-2017),需根据DLMC对DLBM进行批量修改,如旱地是0103,其他林地是0307等,共计19种用地类型。 问…

【ArcGIS微课1000例】0055:根据图层创建自定义图例符号案例教程

在利用ArcGIS作图时,有时候需要根据线状或面状图层自己的矢量形状去创建图例项目符号,本文讲解根据图层创建自定义图例符号。 本实验使用的数据为配套案例数据包中的0055.rar中的水库数据。 文章目录 1. 添加“新建图例图面形状”工具2. 根据图层形状创建符号3. 绘制形状符号…

真魔法!图形化管理 Kafka 超轻量的自动化工具

Kafka Magic[1] 是一个用于处理 Apache Kafka 集群的 GUI 工具。它可以查找和显示消息、在 Topic 之间转换和移动消息、查看和更新模式、管理 Topic 以及自动化复杂任务。Kafka Magic 通过方便的用户界面促进 Topic 管理、QA 和集成测试。Kafka Magic Community Edition 可免费…

【FME实战教程】001:FME2020中文安装图文教程(附安装包下载)

文章目录1. 安装license2. 安装FME Desktop3. 安装中文语言4. FME软件下载地址1. 安装license 打开软件安装包中的fme-flexnet-win-x64.msi,如下图所示: 点击Next。 点击Next。 单击install。 点击finish,完成。 (1)修…

算法导论 第三部分——基本数据结构——第14章:数据结构的扩张

本章通过扩张红黑树构造出两种数据结构:动态顺序统计和区间树。 1、动态顺序统计:查找倒数第i小的数据 复杂度为 lg(n) 为什么是扩张红黑树而不是搜索二叉树或者二叉树? 相对于搜索二叉树,红黑树的平衡性更好,高度在l…

/hgfs下无共享文件夹?/mnt下没有hgfs文件夹?vmhgfs-fuse:找不到命令?

前言:最近在使用linux的过程中,需要在宿主操作系统与客户操作系统间建立共享文件夹,遇到了些许问题,在网上参考了许多文章与各种尝试后,现得以解决,分享如下。1、系统环境:宿主操作系统&#xf…

GraphQL入门有

本文将从GraphQL是什么,为什么要使用GraphQL,使用GraphQL创建简单例子,以及GraphQL实战,四个方面对GraphQL进行阐述。说得不对的地方,希望大家指出斧正。 github项目地址:https://github.com/Charming2015/…

对话庄表伟:开源第一课

庄表伟目前就职于华为的开源管理中心。自2014年开源社成立之初,他便友情参与了开源社的筹办工作。2017年,开源社转型为完全由个人成员组成的组织,庄表伟就以个人身份加入了开源社。作为开源社理事,当被问到“为什么要参选”时&…

【FME实战教程】002:FME完美实现CAD数据转shp案例教程(以三调土地利用现状数据为例)

FME完美实现CAD数据转shp案例教程(以三调土地利用数据为例) 文章目录1. cad数据预览2. 转换过程3. shp数据预览1. cad数据预览 2. 转换过程 (1)打开FME Desktop2020中文软件,点击【新建】。 (2&#xff09…

【GlobalMapper精品教程】017:KML generator快速将坐标转为KML文件

本文介绍KML generator软件,并快速将坐标转为KML文件的使用方法,并用globalmapper中打开kml文件加以验证。本专栏配套完整的案例数据包,请打开data017.rar获取软件及数据。 文章目录 1. KML文件介绍2. kml generator软件介绍2.1 单点KML制作2.2 Excel数据KML制作2.3 文本文件…

模型验证组件 FluentValidation

FluentValidation 是 .NET 下的模型验证组件,和 ASP.NET MVC 基于Attribute 声明式验证的不同处,其利用表达式语法链式编程,使得验证组件与实体分开。正如 FluentValidation 的 介绍: A small validation library for .NET that u…

第二届中国PWA开发者日

点击蓝字关注我们活动介绍为加速推动渐进式 Web 应用 (PWA) 在中国的发展,微软与英特尔携手举办“第二届中国 PWA 开发者日”。本次活动邀请一众业界大咖围绕 PWA 展开分享,探讨最新技术进展,及 PWA 生态的实践与落地。期待与您线上相聚。活动…

【GlobalMapper精品教程】018:提取影像数据的范围生成矢量图层

文章目录 1. 加载影像数据2. 生成边界3. 导出矢量范围4. 背景影响边界解决办法1. 加载影像数据 以DSM为例,加载如下所示: 2. 生成边界 在影像图层上右键→图层→【边界框/覆盖-创建图层覆盖框/多边形区要素】,如下图所示: 选择【否】。 边界创建完成。 3. 导出矢量范围 …

在Leangoo里怎么设置看板周期?

设置看板周期有两种方式: 1)点击看板上的看板周期时间直接修改 2)通过菜单 设置看板周期 浏览器访问官网链接:www.leangoo.com 转载于:https://www.cnblogs.com/shineshine/p/5663104.html

consul部署多节点和consul-template部署

一.consul的介绍 1.1consul是什么? Consul是HashiCorp公司推出的开源工具,用于实现分布式系统的服务发现与配置。 Consul是分布式的、高可用的、可横向扩展的。它具备以下特性 : service discovery:consul通过DNS或者HTTP接口使服务注册和服务发现变的很容易,一些外…

基于ABP实现DDD

什么是DDD呢?领域驱动设计[DDD]是一种针对复杂需求的软件开发方法。将软件实现与不断发展的模型联系起来,专注于核心领域逻辑,而不是基础设施细节。DDD适用于复杂领域和大规模应用,而不是简单的CRUD应用。它有助于建立一个灵活、模…

【GlobalMapper精品教程】019:基于DSM提取离散随机点的高程信息

本文讲解在globalmapper中,基于DSM提取离散随机点的高程信息,配套数据为data019.rar。 文章目录 1. 离散点创建2. 提取离散点高程信息3. 高程标注1. 离散点创建 本文在ArcGIS中,根据给定的范围,随机生成离散点,如下图: 拓展阅读: ArcGIS根据范围创建随机点教程:【ArcG…

shell脚本注意点

2019独角兽企业重金招聘Python工程师标准>>> 直接命令行写脚本的时候,可以用 ; 分割,或 也可以直接回车,然后在继续写脚本在使用 方括号[ ] 的时候,里面空格两边都必须要有空格,比如 [ $a -gt 3 ] 在方括号…