• 闭包有两部分组成,一个是当前的执行上下文A,一个是在该执行上下文中创建的函数B
  • 当B执行的时候引用了当前执行上下文A中的变量就会产出闭包
  • 当一个值失去引用的时候就会会标记,被垃圾收集回收机回收并释放空间
  • 闭包的本质就是在函数外部保持内部变量的引用,从而阻止垃圾回收
  • 调用栈的并不会影响作用域链,函数调用栈是在执行时才确定,而作用域规则是在代码编译阶段就已经确定了
  • MDN定义:闭包是指这样的作用域foo,它包含了一个函数fn,这个函数fn1可以调用被这个作用域所封闭的变量a、函数等内容

    1. function one() {
    2. var a = 1;
    3. var b = 2;
    4. function two() {
    5. var c = 3;
    6. debugger;
    7. console.log(a,c);
    8. }
    9. return two;
    10. }
    11. let two = one();
    12. two();

    闭包的用途

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

    闭包的优化

  • 中间没用到的变量闭包会被忽略 ```javascript function one() { var a = 1; function two() {

    1. var b = 2;
    2. function three() {
    3. var c = 3;
    4. debugger;
    5. console.log(a, b, c);
    6. }
    7. three();

    } two(); } one();

function one() { var a = 1; function two() { var b = 2; function three() { var c = 3; debugger; console.log(a, c); } three(); } two(); } one();

  1. <a name="hrtS9"></a>
  2. # 闭包的应用场景
  3. - return返回一个函数
  4. ```javascript
  5. var n = 10
  6. function fn(){
  7. var n =20
  8. function f() {
  9. n++;
  10. console.log(n)
  11. }
  12. return f
  13. }
  14. var x = fn()
  15. x() // 21
  16. //这里的 return f, f()就是一个闭包,存在上级作用域的引用
  • 函数作为参数 ```javascript var a = ‘heisehuoyan’ function foo(){ var a = ‘foo’ function fo(){
    1. console.log(a)
    } return fo }

function f(p){ var a = ‘f’ p() } f(foo()) /* 输出

  • foo / ```
  • 自执行函数 ```javascript var n = ‘heisehuoyan’; (function p(){ console.log(n) })() /* 输出
  • heisehuoyan / //同样也是产生了闭包p(),存在 window下的引用 n ```
  • 循环赋值

    1. for(var i = 0; i<10; i++){
    2. (function(j){
    3. setTimeout(function(){
    4. console.log(j)
    5. }, 1000)
    6. })(i)
    7. }
    8. /*
    9. 因为存在闭包的原因上面能依次输出1\~10,闭包形成了10个互不干扰的私有作用域。将外层的自执行函数去掉后就不存在外部作用域的引用了,输出的结果就是连续的 10。为什么会连续输出10,因为 JS 是单线程的遇到异步的代码不会先执行(会入栈),等到同步的代码执行完 i++ 到 10时,异步代码才开始执行此时的 i=10 输出的都是 10
    10. */

    使用闭包需要注意

    闭包容易导致内存泄漏。闭包会携带包含其它的函数作用域,因此会比其他函数占用更多的内存。过度使用闭包会导致内存占用过多,所以要谨慎使用闭包

    闭包是怎么回收的

  • 通常,如果引用闭包的函数是一个全局变量,那么闭包会一直存在直到页面关闭;但如果这个闭包以后不再使用的话,就会造成内存泄漏。

  • 如果引用闭包的函数是个局部变量,等函数销毁后,在下次 JavaScript 引擎执行垃圾回收时,判断闭包这块内容如果已经不再被使用了,那么 JavaScript 引擎的垃圾回收器就会回收这块内存。

使用闭包的时候,你要尽量注意一个原则:如果该闭包会一直使用,那么它可以作为全局变量而存在;但如果使用频率不高,而且占用内存又比较大的话,那就尽量让它成为一个局部变量。

面试 | JS 闭包经典使用场景和含闭包必刷题