ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

真正理解一下闭包!

2021-07-29 15:00:05  阅读:204  来源: 互联网

标签:闭包 function displayName 函数 作用域 访问 理解 真正


闭包,可谓是面试常谈,什么是闭包 ?闭包是用来做什么的 ?

之前在准备一些面试时,我都会搜查一番,< 闭包 > 在各种技术书籍和博客中,都有着各种解释和分析,

比如:“ 闭包就是指有权访问另一个函数作用域中的变量的函数 ” ;“ 声明在一个函数中的函数,叫做闭包函数 ”,“ 闭包函数会造成内存泄漏” 等等等, 随后又分析了几个代码案例,然后,“ 嗯,原来这就是闭包啊! ”。

但其实真实情况,还是似懂非懂,但是在 实际应用中,闭包很常见,今天,再来彻底总结一下闭包,到底它是个神马玩意er

参考了一些大佬的文章和一些问答讨论,附上:

js中的闭包是什么?:https://zhuanlan.zhihu.com/p/22486908

难于理解的闭包 : https://segmentfault.com/q/1010000040284976?utm_source=sf-homepage

学习javascript闭包 : https://www.ruanyifeng.com/blog/2009/08/learning_javascript_closures.html

1. 如何定义闭包

先来看一个简单的例子:

简化一下,假设以下三行代码,放在一个立即执行函数

    var name = "Mozilla";

    function displayName() {
      alert(name);
    }

如上代码,在displayName 函数内部,可以访问到定义在这个立即执行的函数中的 name属性,这就形成了一个简单的闭包

( displayName函数+函数内部可以访问到的name属性 )

很多人可能会有疑惑,这个就是闭包吗,就这 ??

“ 我怎么听说闭包是需要函数套函数,然后 return 函数的呀!” 就像下面

    function makeFunc() {
      var name = "Mozilla";

      function displayName() {
        alert(name);
      }
      return displayName;
    }

    var myFunc = makeFunc();
    myFunc();

这确实也有闭包,displayName函数和name属性就组成了一个闭包

为什么需要函数套函数呢?

是因为需要局部变量,所以把name放在 了一个函数里,如果不把name放在一个函数内,name就变成一个全局变量了

有些人看来,闭包,闭包,**包,**一定要有什么被包起来才行,然鹅,这只是一个翻译问题,闭包的原文是 closure,与包无关。

可以理解为,函数套函数只是为了造出一个局部变量,跟闭包无关

为什么要return呢

因为如果不 return,你就无法使用这个闭包。把 return displayName 改成 window.displayName = displayName 也是一样的,只要让外面可以访问到这个 displayName 函数就行了。

所以 return displayName 只是为了 displayName 能被使用,也跟闭包无关。

来看一下MDN,对于闭包的解释

< 一个函数和对其周围状态(lexical environment,词法环境)的引用捆绑在一起(或者说函数被引用包围),这样的组合就是闭包closure)。也就是说,闭包让你可以在一个内层函数中访问到其外层函数的作用域。在 JavaScript 中,每当创建一个函数,闭包就会在函数创建的同时被创建出来。 >

简化一下, 函数和周围状态的引用捆绑,这样的组合就是闭包,由此看来,闭包并不是 “有权访问另一个函数作用域中的变量的函数 ”,闭包甚至都不是函数啊,而是一个组合,正如你不能说情侣是某个男人或某个女人一样, 情侣是两个有情人的组合。

“函数和周围状态”, 函数容易理解,需要解释的就只有“周围状态”了。 MDN 对此的解释是“词法环境”,但是没有给出词法环境的定义,按照我的理解,词法环境由当前位置可访问的全部变量构成,至于哪些变量是“可访问的”,这就是作用域的知识了, 这样看来,考察闭包,无非就是在考察作用域,换句话说,闭包是 JS 函数作用域的副产品

所以这里可以粗略的认为

闭包 ≈ 函数 + 此函数内部能访问的全部变量

2. 闭包的作用

闭包,常用来间接访问一个变量(或者说将一个变量隐藏),为什么要间接访问(隐藏)呢,我直接访问不行吗?举个例子吧

假设一个游戏,在其中写 「还剩几条命的代码」,如果不用闭包,可以直接写一个全局变量(暴露出来,不隐藏)

    window.lives = 30 // 还有三十条命

这样看起来很不妥,万一不小心把这个值改成 -1 了怎么办。所以我们不能让别人 直接访问 这个变量。怎么办呢?

用局部变量,

但是局部变量别人又访问不到了,怎么办?

暴露一个访问器(函数),让别人通过这个访问器(函数),间接访问

    ! function () {

      var lives = 50

      window.奖励一条命 = function () {
        lives += 1
      }

      window.死一条命 = function () {
        lives -= 1
      }

    }()

那么在其他的 JS 文件,就可以使用 window.奖励一条命( ) 来涨命,使用 window.死一条命( ) 来让角色掉一条命。

这样,通过两个函数,可以间接访问到lives这个变量,同时,这样生成了闭包( 函数 + 此函数内部能访问的全部变量 )

3.闭包有何特性

鉴于 JS 代码中至少会有全局作用域存在,所以任何函数都有“周围状态”,也就是说你所见的每一个函数,实际上都属于某个闭包的一部分,而闭包的特性就是 JS 中作用域的特性:函数可以访问其外部环境中的变量。
这简直就是一句废话,到此为止,闭包的特别之处还没有体现出来——因为闭包根本没有特别之处。

学习“闭包”,首先应该搞清楚“作用域”

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

    var n = 999;

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

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

    function f1() {
      var n = 999;
    }
    alert(n); // n is not defined

But,我就要在函数外部得到函数内的局部变量,怎么办呢?

好吧,那只能变通一下,

在函数内部,再定义一个函数

    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 = 99

      function f2() {
        alert(n);
      }
      return f2;
    }
    var result = f1();
    result(); // 999

如上,就是我们所熟悉的闭包了,这样看来确实呀,闭包的特性就是 JS 中作用域的特性

内存泄露的谣言!!

另外,关于闭包,很多人会提到内存泄露,那问你一下,什么是内存泄露呢,可能你也说不上个一二三四,人云亦云罢了

那么到底什么是内存泄露,解释一下:

对于持续运行的服务进程(daemon),必须及时释放不再用到的内存。否则,内存占用越来越高,轻则影响系统性能,重则导致进程崩溃。不再用到的内存,没有及时释放,就叫做内存泄漏(memory leak)。

这里简单来说就是 你用不到(访问不到)的变量,依然占居着内存空间,不能被再次利用起来,这就造成了内存泄露。

但是对于闭包来说,闭包里面的变量明明就是我们需要的变量(lives),用到的变量,凭什么说是内存泄露啊,所以,谣言,谣言没错了

那这个谣言是如何来的呢?

因为 IE。IE 在我们使用完闭包之后,依然回收不了闭包里面引用的变量。

但是这是 IE 的问题,不是闭包的问题。具体可参考

上边这些是看了一些博客资料,了解闭包后,个人的一个总结记录,如有问题,欢迎大家留言评论,多多指教!

标签:闭包,function,displayName,函数,作用域,访问,理解,真正
来源: https://blog.csdn.net/m0_49159526/article/details/119210998

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有