闭包

作者: 糕糕AA | 来源:发表于2019-06-27 14:14 被阅读0次

    什么是闭包

    • 就是在一个外部函数内部创建另一个函数

    • 全局调用内部函数的时候可以访问到外部函数的局部变量,并使用!

    闭包的使用场景:

    • setTimeout

    • 回调

    • 封装变量

    • 事件处理程序

    • 模块模式

    优缺点:

    • 逻辑连续,当闭包作为另一个函数调用参数时,避免脱离当前逻辑而单独编写额外逻辑

    • 方便调用上下文的局部变量。

    • 加强封装性,是第2点的延伸,可以达到对变量的保护作用。

    • 延长作用域链:局部变量存在函数中,函数使用完后,局部变量会自动释放,闭包后,局部变量还存在被调用的可能,没有被及时释放,作用域链延长。

    闭包会使得函数中的变量都被保存在内存中,内存消耗很大,在IE中可能导致内存泄露。 (动态存储分配函数内存空间,在使用完毕后未释放,导致一直占用该内存单元,直到程序结束)

    - 垃圾清除

    1. 标记清除(常用):垃圾回收机制在运行时会给存储在内存中的所有变量加上标记(可以使用任何标记方式)。然后去掉环境中的变量和被环境变中的变量引用的变量的标记。而在此之后被加上标记的变量被视为准备删除的变量,原因是环境中的变量已经无法访问到这些变量了。最后垃圾收集器完成内存清除工作,销毁带标记的值并回收他们所占用的内存空间

    2. 引用计数:reference counting 含义是跟踪记录每个值被引用的次数。当声明了一个变量并将一个引用类型值赋给该变量时,则这个值的引用次数为1.如果同一个值又被赋给另一个变量,引用次数加1.相反的,变量包含这个值得引用变量又取得另一个值,则这个值的引用次数减1.当引用次数为0时,将其占用内存回收。(循环引用的变量和函数无法回收)

    3. 解决:将完成的函数或变量重置为null。

    闭包的模块应用分析

    在 JavaScript 中,闭包最常见的应用是模块模式。模块允许你定义外部不可见的私有实现 细节(变量、函数),同时也可以提供允许从外部访问的公开 API。

    function User(){    
        var username, password; 
     
        function doLogin(user,pw) {       
            username = user;         
            password = pw; 
     
            // 执行剩下的登录工作    
        }    
        var publicAPI = {      
            login: doLogin  
        }; 
     
        return publicAPI;
    } 
     
    // 创建一个User模块实例
    var fred = User();  // 。User() 只是一个函数,而不是需要实例化的类,所以只是正常调用就可 以了。使用 new 是不合适的,实际上也是浪费资源。
     
    fred.login( "fred", "12Battery34!" );
    // 函数User() 用 作 外 层 作 用 域, 持 有 变 量username 和 password,以及内层的函数 doLogin();这些都是这个 User 模块私有的内部细节,无法从外部访问。
    

    执行 User() 创建了 User 模块的一个实例,这创建了一个新的作用域,因而创建了所有内 层变量 / 函数的一个新副本。我们将这个实例赋给 fred。如果再次运行 User(),那么会得 到一个不同于 fred 的全新实例。

    内层的函数 doLogin() 在 username 和 password 上有一个闭包,这意味着即使在 User() 函 数运行完毕之后,函数 doLogin() 也保持着对它们的访问权。

    publicAPI 是带有一个属性 / 方法 login 的对象,login 是对内层函数 doLogin() 的一个引 用。当我们从 User() 返回 publicAPI 时,它就变成了我们命名为 fred 的那个实例。

    此时,外层的函数 User() 已经运行完毕。我们通常认为像 username 和 password 这样的内 层变量也就随之消失了。但上述示例并不会这样,因为 login() 函数的内部有一个可以使 得它们依然保持活跃的闭包。

    这就是我们可以调用 fred.login(..) 的原因,这等同于调用内层 doLogin(..),并且 fred. login(..) 仍然可以访问内层变量 username 和 password。

    相关文章

      网友评论

          本文标题:闭包

          本文链接:https://www.haomeiwen.com/subject/kbfrcctx.html