闭包_理解闭包

闭包允许函数访问并操作函数外部的变量。只要变量或函数存在声明时的作用域内,闭包即可使用函数能够访问这些变量或函数。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="../unitl/test.js"></script>
    <style>
        #results li.pass {color:green;}
        #results li.fail {color:red;}
    </style>


</head>

<body>
    <ul id="results"></ul>
</body>
<script>
    //在全局作用域定义一个变量
    var outerValue = "ninja";

    function outerFunction() {

        //在全局作用域中声明函数
        assert(outerValue==="ninja","I can see the ninja.");

    }

    // 执行该函数
    outerFunction();
</script>
</html>

在本例中,在统一作用域中声明了变量outerValue及外部函数outerFunction-- 本例中,是全局作用域,然后,执行外部函数outerFunction。

如上图,在函数可以“看见”并访问变量outerValue。我们可能写过上百次这样的代码,但是却没有意识到起始我们正在创建一个闭包!
因为外部变量outerValue和外部函数outerFunction都是在全局作用域中声明的,该作用域(实际上实际上就是一个闭包)从为消失(只要应用属于运行状态)。这也不足为奇了。该函数可以访问到外部变量。因为他仍然在作用域内并且是可见的。
虽然闭包存在,但是闭包的优势仍然不明显。让我们在接下来的例子中加点料。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="../unitl/test.js"></script>
    <style>
        #results li.pass {color:green;}
        #results li.fail {color:red;}
    </style>


</head>

<body>
    <ul id="results"></ul>
</body>
<script>
    //在全局作用域定义一个变量
    var outerValue = "samurai";

    var later;

    function outerFunction() {

        //在全局作用域中声明函数

        var innerValue = "ninja";



        function innerFunction() {
            
            //在outerFunction函数中声明一个内部函数,声明该内部函数时,innerValue是在内部函数的作用域内的。
            assert(outerValue==="samurai","I can see the samurai");
            assert(innerValue==="ninja","I can see the ninja");


        }

        //将内部函数innerFunction的引用存储在变量later上,因为later在全局作用域内的,所以我们可以对她进行调用。
        later = innerFunction;

    }

    // 调用outerFunction函数内,创建内部函数innerFunction,并将内部函数赋值给later。
    outerFunction();

    //通过later调用内部函数。我们不能直接调用内部函数,因为他有作用域(和innerValue一起)被限制在外部函数outerFunction之内。
    later();



   
    
</script>
</html>      

仔细研究一下内部函数innerFunction中的代码,看看我们能否预测会发生什么。

  • 第一个断言肯定会通过,因为外部变量outerValue在全局作用域内,并且在任何地方都可见。但是第二个断言呢?
  • 外部函数执行后,我们通过将内部函数的引用赋值给全局变量later,再通过later调用内部函数。
  • 当内部函数执行时,外部函数的作用域已经不存在了,并且在通过later调用内部函数时,外部函数的作用域已不可见了。
  • 所以我们可以很好的预见断言失败,因为内部变量innerValue肯定是undefined,对吗?
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>另一个闭包的例子</title>
    <script src="../unitl/test.js"></script>
    <style>
        #results li.pass {color:green;}
        #results li.fail {color:red;}
    </style>


</head>

<body>
    <ul id="results"></ul>
</body>
<script>
    //在全局作用域定义一个变量
    var outerValue = "samurai";

    var later;

    function outerFunction() {

        //在全局作用域中声明函数

        var innerValue = "ninja";



        function innerFunction() {
            
            //在outerFunction函数中声明一个内部函数,声明该内部函数时,innerValue是在内部函数的作用域内的。
            assert(outerValue==="samurai","I can see the samurai");
            assert(innerValue==="ninja","I can see the ninja");


        }

        //将内部函数innerFunction的引用存储在变量later上,因为later在全局作用域内的,所以我们可以对她进行调用。![](https://img2020.cnblogs.com/blog/1072158/202012/1072158-20201222143249993-1340542549.jpg)


        later = innerFunction;

    }

    // 调用outerFunction函数内,创建内部函数innerFunction,并将内部函数赋值给later。
    outerFunction();

    //通过later调用内部函数。我们不能直接调用内部函数,因为他有作用域(和innerValue一起)被限制在外部函数outerFunction之内。
    later();



   
    
</script>
</html>

但是,当我们执行完测试时,我们看到如下图所示。

是什么魔法使得内部函数的作用域消失之后再执行内部函数时,其内部变量仍然存在呢?
当在外部函数中声明内部函数时,不仅定义了函数的声明,而且还创建了一个闭包。该闭包不仅包含了函数的声明,还包含了在函数声明时该作用域中的所有变量。
当最终执行内部函数时,尽管声明时的作用域已经消失了,但是通过闭包,仍然能够访问该作用域。如下图所示。


正如上图一样,只要内部函数一直存在,内部函数的闭包就一直保存该函数的作用域中的变量。
这就是闭包。闭包创建了被定义时的作用域内的变量和函数的安全域,因为函数获取执行时所需内容。该安全域与函数本身一起包含了函数的变量。
虽然这些结构不容易看见(没有包含这么多信息的闭包对象可以进行观察),存储和引用这些信息会直接影响性能。谨记每一个通过闭包访问变量的函数都具有一个作用域链,作用域链包含闭包的所有信息,这一点非常重要。因此,虽然闭包是非常有用的,但不能过度使用。使用闭包时,所有的信息都会存储在内存中,直到JavaScript引擎确保这些信息不在使用(可以安全地进行垃圾回收)或页面卸载时,才会清理这些信息。

posted @ 2020-12-22 15:52  yongjar  阅读(75)  评论(0编辑  收藏  举报