深入理解js闭包

闭包(closure)是Javascript语言的一个难点,也是它的特色,很多高级应用都要依靠闭包实现。

下面就是我的学习笔记,对于Javascript初学者应该是很有用的。

一、变量的作用域

要理解闭包,首先必须理解Javascript特殊的变量作用域。

变量的作用域无非就是两种:全局变量和局部变量。

Javascript语言的特殊之处,就在于函数内部可以直接读取全局变量。

  var n=999;

  function f1(){
    alert(n);
  }

  f1(); // 999

另一方面,在函数外部自然无法读取函数内的局部变量。

  function f1(){
    var n=999;
  }

  alert(n); // error

这里有一个地方需要注意,函数内部声明变量的时候,一定要使用var命令。如果不用的话,你实际上声明了一个全局变量!

  function f1(){
    n=999;
  }

  f1();

  alert(n); // 999

二、如何从外部读取局部变量?

出于种种原因,我们有时候需要得到函数内的局部变量。但是,前面已经说过了,正常情况下,这是办不到的,只有通过变通方法才能实现。

那就是在函数的内部,再定义一个函数。

  function f1(){

    var n=999;

    function f2(){
      alert(n); // 999
    }

  }

在上面的代码中,函数f2就被包括在函数f1内部,这时f1内部的所有局部变量,对f2都是可见的。但是反过来就不行,f2内部的局部变量,对f1就是不可见的。这就是Javascript语言特有的"链式作用域"结构(chain scope),子对象会一级一级地向上寻找所有父对象的变量。所以,父对象的所有变量,对子对象都是可见的,反之则不成立。

既然f2可以读取f1中的局部变量,那么只要把f2作为返回值,我们不就可以在f1外部读取它的内部变量了吗!

  function f1(){

    var n=999;

    function f2(){
      alert(n); 
    }

    return f2;

  }

  var result=f1();

  result(); // 999

三、闭包的概念

上一节代码中的f2函数,就是闭包。

各种专业文献上的"闭包"(closure)定义非常抽象,很难看懂。我的理解是,闭包就是能够读取其他函数内部变量的函数。

由于在Javascript语言中,只有函数内部的子函数才能读取局部变量,因此可以把闭包简单理解成"定义在一个函数内部的函数"。

所以,在本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁。

四、闭包的用途

闭包可以用在许多地方。它的最大用处有两个,一个是前面提到的可以读取函数内部的变量,另一个就是让这些变量的值始终保持在内存中。

怎么来理解这句话呢?请看下面的代码。

  function f1(){

    var n=999;

    nAdd=function(){n+=1}

    function f2(){
      alert(n);
    }

    return f2;

  }

  var result=f1();

  result(); // 999

  nAdd();

  result(); // 1000

在这段代码中,result实际上就是闭包f2函数。它一共运行了两次,第一次的值是999,第二次的值是1000。这证明了,函数f1中的局部变量n一直保存在内存中,并没有在f1调用后被自动清除。

为什么会这样呢?原因就在于f1是f2的父函数,而f2被赋给了一个全局变量,这导致f2始终在内存中,而f2的存在依赖于f1,因此f1也始终在内存中,不会在调用结束后,被垃圾回收机制(garbage collection)回收。

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

五、使用闭包的注意点

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

2)闭包会在父函数外部,改变父函数内部变量的值。所以,如果你把父函数当作对象(object)使用,把闭包当作它的公用方法(Public Method),把内部变量当作它的私有属性(private value),这时一定要小心,不要随便改变父函数内部变量的值。

六、思考题

如果你能理解下面两段代码的运行结果,应该就算理解闭包的运行机制了。

代码片段一。

  var name = "The Window";

  var object = {
    name : "My Object",

    getNameFunc : function(){
      return function(){
        return this.name;
      };

    }

  };

  alert(object.getNameFunc()());


代码片段二。

  var name = "The Window";

  var object = {
    name : "My Object",

    getNameFunc : function(){
      var that = this;
      return function(){
        return that.name;
      };

    }

  };

  alert(object.getNameFunc()());

 

原文网址:http://www.ruanyifeng.com/blog/2009/08/learning_javascript_closures.html

转载于:https://www.cnblogs.com/wei-fang/p/4747032.html

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

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

相关文章

71页《乌镇智库:全球人工智能发展报告(2018)》PDF下载

来源:专知【导读】人工智能热潮之下,斯坦福、阿里等纷纷出台人工智能报告。乌镇智库已连续发布三年《全球人工智能发展报告》,以宏观视角纵览全球人工智能发展,从产业、融资、技术、教育和应用等多个角度展现人工智能在全球的发展…

Windows实用技巧

Windows常用命令,亲测可用 创建文件夹Test,命令为:mkdir Test 盘符: 进入系统中的某个盘,例如E:进入E盘cd 文件夹位置 进入某个文件夹,需要先进入盘符,即完成第一步,例如…

python进程、线程、协程

通过学习bi站 蚂蚁学Python 老师视频总结文档,仅用于学习。。。 python进程、线程、协程 多线程:threading,利用CPU和IO可以同时执行的原理,不会让CPU干巴巴的等待IO完成 多进程:multiprocessing,利用多核…

一文读懂计算计仿真技术

来源:传感器技术计算机仿真作为分析和研究系统运行行为、揭示系统动态过程和运动规律的一种重要手段和方法, 随着系统科学研究的深入、控制理论、计算技术、计算机科学与技术的发展而形成的一门新兴学科。近年来, 随着信息处理技术的突飞猛进, 使仿真技术得到迅速发…

Chrome Cookie SameSite 属性设置

Chrome Cookie SameSite 设置 Chrome 51 开始,浏览器的 Cookie 新增加了一个SameSite属性,用来防止 CSRF 攻击和用户追踪。 Cookie 的SameSite属性用来限制第三方 Cookie,从而减少安全风险。 它可以设置三个值。 StrictLaxNone Chrome 默认…

胡蜂也会逻辑推理

胡蜂能通过基本的逻辑测试。来源: 中国科学报逻辑推理是一种复杂的行为,通常被认为仅限于具有复杂神经系统的动物。但一项新的研究表明,胡蜂可以运用某种逻辑推理。这在无脊椎动物中尚属首次。相关成果日前发表于英国《皇家学会生物学报》。这…

两种方法解决tomcat的 Failed to initialize end point associated with ProtocolHandler [http-apr-8080]...

安装多个tomcat同时启动的时候,端口号冲突。修改下端口号就可以了。转载于:https://www.cnblogs.com/CooderIsCool/p/4749831.html

python asyncio 异步编程---协程

1、协程 官方描述; 协程是子例程的更一般形式。 子例程可以在某一点进入并在另一点退出。 协程则可以在许多不同的点上进入、退出和恢复。 它们可通过 async def 语句来实现。 参见 PEP 492。 协程不是计算机内部提供的,不像进程、线程,由电脑本身提供&a…

二十大未来最有潜力的新材料(绝对经典值得收藏)

来源:新材料在线石墨烯、碳纳米管、非晶合金、泡沫金属、离子液体……20种新材料,为材料工业工业发展带来无限机遇。材料工业是国民经济的基础产业,新材料是材料工业发展的先导,是重要的战略性新兴产业。今天,科技革命…

Unity3D 4.x 使用Mecanim实现动画控制

Unity3D 4.x 版本号之后提供了一种新的动画机制Mecanim,尽管眼下还支持之前的Animation。但看到Unity3D 4.3 预览版里Sprite的动画也是基于Animator的,可知Mecanim将会是以后动画播放的趋势! Mecanim是一种基于状态机的结构,不同的…

全国国家重点实验室分布总览

来源:114产学研在我国的科研平台,企业国家重点实验室、国家重点实验室和省部共建国家重点实验室在科学前沿探索和解决国家重大需求方面发挥了非常重要的作用,在科学研究方面取得不少具有国际先进水平的成果。2018年5月21日,科技部…

(算法)宝石升级问题

题目: 有一块宝石,1级升2级成功率100%,2级升3级成功率80%,3级升4级成功率60%,4级升5级成功率40%,每次升级失败时降回到1级。请问一块1级宝石升到5级平均要多少…

python asyncio 异步编程-协程 2

asyncio 异步编程 官方文档: 中文版:https://docs.python.org/zh-cn/3.8/library/asyncio.html英文本:https://docs.python.org/3.8/library/asyncio.html 1. 事件循环 事件循环 是指主线程每次将执行序列中的任务清空后,就去…

能源枯竭?在能源互联网时代不存在!

曹军威清华大学信息技术研究院研究员、副院长北京智中能源互联网研究院首席科学家来源:DeepTech深科技演绎inSite第十一期节目能量路由器离我们还远吗?曹军威演绎inSite演讲视频链接:以下为曹军威老师演讲文字稿:(根据…

Django3 --- ASGI

1. 什么是WSGI 1.1 CGI 解释 WSGI 之前应该先说一下什么是 CGI(通用网关接口,Common Gateway Interface,CGI),是Web 服务器运行时外部程序的规范 , 是外部扩展应用程序与 Web 服务器交互的一个标准接口。…

itextsharp c# asp.net 生成 pdf 文件

一切的开始必须要有2个dll, 可以通过nuget 包xiazai, 关键字是itextsharp. using iTextSharp.text; using iTextSharp.text.pdf; FileStream fs new FileStream(Server.MapPath("pdf") "\\" "First PDF document6.pdf"…

网络安全:等保2.0落地在即,触发五百亿新增市场

报告数据来源:华创证券、东方财富、东吴证券前 言:据公安部十一局七处处长祝国邦:《网络安全等级保护技术》2.0版本将于5月13日发布。相比等保1.0只针对网络和信息系统,等保2.0把云计算、大数据、物联网等新业态也纳入了监管&…

Django3 --- async

官方文档:https://docs.djangoproject.com/en/3.2/releases/3.0/ Django 3.0 通过提供对作为ASGI应用程序运行的支持,开始了我们使 Django 完全具有异步能力的旅程。 Django 3.1于2020年8月4日发布!从3.1版本开始,Django将逐步原…

产业|一文读懂自动驾驶汽车产业链上下游

来源: 亿欧自动驾驶汽车它的产业链上下游已经出现支撑公司,并在逐渐走向成熟。自动驾驶分级标准 关于自动驾驶的分级,主要有SAE(美国机动车工程师学会)标准和NHTSA(国家公路交通安全管理局)两个…

【python asyncio 运行报错】:raise RuntimeError(‘There is no current event loop in thread %r‘)

代码: # 执行第一个协程程序 asyncio.run(S.crawl_url())select_date S.select_date() select_keyword S.select_keyword(select_date) # 列表# 第二个协程 loop asyncio.get_event_loop() loop.run_until_complete(asyncio.wait([S.parse_html(url) for url i…