什么是闭包
-
就是在一个外部函数内部创建另一个函数
-
全局调用内部函数的时候可以访问到外部函数的局部变量,并使用!
-
setTimeout
-
回调
-
封装变量
-
事件处理程序
-
模块模式
优缺点:
-
逻辑连续,当闭包作为另一个函数调用参数时,避免脱离当前逻辑而单独编写额外逻辑
-
方便调用上下文的局部变量。
-
加强封装性,是第2点的延伸,可以达到对变量的保护作用。
-
延长作用域链:局部变量存在函数中,函数使用完后,局部变量会自动释放,闭包后,局部变量还存在被调用的可能,没有被及时释放,作用域链延长。
闭包会使得函数中的变量都被保存在内存中,内存消耗很大,在IE中可能导致内存泄露。 (动态存储分配函数内存空间,在使用完毕后未释放,导致一直占用该内存单元,直到程序结束)
- 垃圾清除
-
标记清除(常用):垃圾回收机制在运行时会给存储在内存中的所有变量加上标记(可以使用任何标记方式)。然后去掉环境中的变量和被环境变中的变量引用的变量的标记。而在此之后被加上标记的变量被视为准备删除的变量,原因是环境中的变量已经无法访问到这些变量了。最后垃圾收集器完成内存清除工作,销毁带标记的值并回收他们所占用的内存空间
-
引用计数:reference counting 含义是跟踪记录每个值被引用的次数。当声明了一个变量并将一个引用类型值赋给该变量时,则这个值的引用次数为1.如果同一个值又被赋给另一个变量,引用次数加1.相反的,变量包含这个值得引用变量又取得另一个值,则这个值的引用次数减1.当引用次数为0时,将其占用内存回收。(循环引用的变量和函数无法回收)
-
解决:将完成的函数或变量重置为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。
网友评论