← 返回首页
Javascript基础教程(十九)
发表时间:2020-03-28 14:09:27
讲解Javascript的闭包

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

1.为什么要使用闭包

我们知道在函数内使用var声明的变量,是函数内的局部变量。那么函数外部自然无法读取函数内的局部变量。 例如:

<script>
    function fn(){
        var num = 100;
    }

    console.log(num);
</script>

运行结果:
Uncaught ReferenceError: num is not defined

如何从外部读取局部变量?我们可以在函数的内部,再定义一个函数,再返回这个内部函数的引用。例如:

<script>
    function fn(){
        var num = 100;
        function test(){
            return num;
        }
        return test;
    }
    var f = fn();
    console.log(f());
</script>

运行结果:
100

上面的test函数,就是闭包。

2.闭包的本质

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

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

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

3.使用闭包的好处

闭包可以用在许多地方。它的最大用处有两点: - 外部函数读取其他函数内部变量的函数 - 让某些变量的值始终保持在内存中

外部函数读取其他函数内部变量的函数,例如:

<script>
    function fn(){
        var num = 100;
        function test(){
            return num;
        }
        return test;
    }

    function haha(){
        let f = fn();
        let n = f();
        console.log(n); //haha函数获取到fn函数内的局部变量。
    }
    haha();

</script>
运行结果:
100

让某些变量的值始终保持在内存中,例如:

<script>
    function fn(){
        var num = 100;
        increasement = function(){
            num++;
        };
        function test(){
            console.log(num);
            return num;
        }
        return test;
    }

    var f = fn();
    f();
    increasement();
    f();
</script>

运行结果:
100
101

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

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

4.返回匿名函数的闭包

闭包还可以是返回匿名函数,上面的例子改写如下:

<script>
    function fn(){
        var num = 100;
        increasement = function(){
            num++;
        };
        return function (){
            console.log(num);
            return num;
        }
    }

    var f = fn();
    f();
    increasement();
    f();
</script>

运行结果:
100
101

注意:如果f一旦重新赋值,那么那些始终保持在内存中变量的值,也将被重新初始化。例如:

<script>
    function fn(){
        var num = 100;
        increasement = function(){
            num++;
        };
        return function (){
            console.log(num);
            return num;
        }
    }

    var f = fn();
    f();
    increasement();
    f = fn(); //f被重新赋值
    f();
</script>

运行结果:
100
100