JS闭包到底是什么包?

先来看看文献中的定义

  • JavaScript权威指南

    “函数定义时的作用域链到函数执行时依然有效。”

  • MDN技术文档

    “闭包是函数和声明该函数的词法环境的组合。”

  • 你不知道的JavaScript

    ”当函数可以记住并访问所在的词法作用域时,就产生了闭包,即使函数是在当前词法作用域之外执行。“

  • CSS-Tricks

    “当你在一个函数内创建了另一个函数时,你就创建了一个闭包。内部的函数就是闭包。这个闭包通常会被返回,所以你能够在未来使用外部函数的变量。 “


想深入了解闭包,首先要搞清这些概念:

  • 词法作用域

    顾名思义,就是定义在词法阶段的作用域。无论函数在哪里调用、何时被调用,它的词法作用域始终由函数被声明时所处的位置决定。

  • 函数作用域

    JS主要分全局作用域和函数作用域,函数作用域是根据“最小暴露”原则设计出来的,目的是为了隐藏私有变量,规避冲突。因为外部作用域无法访问函数内部的任何内容。

  • 内存垃圾回收机制

    内存的生命周期有三步:分配内存、使用内存、不需要时释放内存。垃圾回收就是根据一定的算法来大体判定“不再需要”的内存,注意它具有局限性。


举几个例子

例子1

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
function foo() {
    car a = 2;
    
    function bar() {
        console.log(a);
    }
    
    return bar;
}

var baz = foo();  /* 这一步相当于把bar赋值给了baz */

//下面这一步相当于执行了bar(),bar()引用了foo()的作用域,
//所以foo()没有被内存管理系统当作垃圾回收,bar()对该作用域的引用就叫闭包。
baz(); /*  输出2  */ 

例子2

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
function makeAdder(x) {
    return function(y) {
        return x + y;
    };
}

//下面这步相当于把内部函数赋值给了一个新值,
//并指定了外部函数的参数x的值,但内部函数y的值还没有指定。
var add3 = makeAdder(3);  
var add9 = makeAdder(9);

//最后这一步指定了内部函数的参数y的值,因此就可以返回x + y的值了。
console.log(add3(2));  /* 5 */ 
console.log(add9(2));  /* 11 */

例子3

1
2
3
4
5
6
//立即调用函数的返回值赋值给myCounter
var myCounter = (function() {
                        var counter = 0;
                        return function(){ return counter++;};
                        }());

自己的话总结

JavaScript闭包就是一个母函数内部的子函数保存了对母函数的私有变量的引用,从而避免母函数的私有变量被内存垃圾回收机制清除掉,相当于创造了一种私有变量永久储存的方式,有效避免了私有变量污染全局变量。子函数的这种引用可以在任何地方使用,即使不在最初定义的词法作用域里也可以实现。这种引用机制就是闭包。