zoukankan      html  css  js  c++  java
  • 闭包_理解闭包

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

    <!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引擎确保这些信息不在使用(可以安全地进行垃圾回收)或页面卸载时,才会清理这些信息。

  • 相关阅读:
    JS实战 · 表单验证
    JS实战 · 仿css样式选择器
    JS实战 ·  收缩菜单表单布局
    cookie自动登录的实现
    redis 3.2.5单机版安装、使用、systemctl管理Redis启动、停止、开机启动
    yum问题解决
    配置yum镜像源
    shell笔记
    CCIE总结:路由器、交换机
    云主机如何挂在磁盘
  • 原文地址:https://www.cnblogs.com/jamal/p/14173482.html
Copyright © 2011-2022 走看看