维基百科摘要:
在计算机科学中,闭包(英語:Closure),又稱词法闭包(Lexical Closure)或函數閉包(function closures),是引用了自由变量的函数。这个被引用的自由变量将和这个函数一同存在,即使已经离开了创造它的环境也不例外。所以,有另一种说法认为闭包是由函数和与其相关的引用环境组合而成的实体。闭包在运行时可以有多个实例,不同的引用环境和相同的函数组合可以产生不同的实例。
百度百科摘要:
闭包就是能够读取其他函数内部变量的函数。例如在javascript中,只有函数内部的子函数才能读取局部变量,所以闭包可以理解成“定义在一个函数内部的函数“。在本质上,闭包是将函数内部和函数外部连接起来的桥梁。
一、定义:一种解决问题的办法,一种特殊结构 ,一种机制。
- 包含两层含义要执行的代码块(自有对象机子有对象引用的对象)和自由变量的作用域。
- 当一个嵌套的内部函数引用了外部函数的变量或函数时,即产生闭包
二、作用:仅能使变量重用又能保护变量不被污染
- 使用函数内部的变量再函数执行完后,仍然存活在内存中(延长了局部变量的生命周期)
- 让函数在外部可以操作(读写)到函数内部的数据(变量/函数)
三、闭包使用方法:
- 预编译阶段明确GO、各外层函数与内层函数AO的相关变量的声明
- 明确外层函数与内层函数的关系:
①用外层函数包裹搜保护的变量和操作变量的内层函数;
②外层函数通过某些方法将内层函数赋值到外层函数之外,如返回值;
③调用外层函数,获得内层函数。- 执行顺序按照解释性语言的由上至下执行,执行函数调用语句时,在执行环境栈生成一个新的元素即新的执行环境记录新的幻术调用,创建其对应的函数作用域对象(AO)。
- 明确内层函数与外层函数AO之间的关系,关系的继承取决于子函数(内层函数)是否存在scope指向父函数(外层函数)。
四、闭包生命周期:
产生
当嵌套的内部函数定义执行完时就产生了(非内部调用时)
死亡
在嵌套的内部函数成为垃圾对象时
f = null
函数执行完毕后,函数内部声明的局部变量一般都不存在了,只有存在于闭包中的变量才可能存在
( 即有某个全局变量的指针还指向着某内部函数的地址 )
五、缺点:浪费空间,致使内存溢出。
内存溢出:一种程序运行出现的错误,当程序运行需要的内存超过了剩余的内存时,就会抛出内存溢出的错误
内存泄漏:占用的内存未有及时释放,内存泄漏累计多了就易导致内存溢出
闭包易造成内存溢出原因:函数执行完后,函数内的局部变量没有释放,导致内层函数对外层函数的scope指向无法释放(作用域链),致使外层函数调用结束但其AO无法释放,最终导致占用内存时间变长。
解决方式:
- 减少闭包使用;
- 及时释放(f = null ==> 让内部函数成为垃圾对象 ==> 回收闭包)
六、示例
var nAdd;
function out(){
var n = 999;
nAdd = function(){
n ++;
}
return function(){
console.log(n);
}
}
// out AO{
n: 999
}
var getN = out();
getN(); // n = 999
// nAdd AO{
// }
nAdd(); // 执行n++ 外层函数声明的n 即 999+1
getN(); // n = 1000
七、应用 JS 模块
将所有的数据和功能都封装到一个函数内部 (私有的),只向外暴漏一个包含n个方法的对象或函数,模块的使用者,只需通过模块暴露的对象,调用方法来实现对应的功能