闭包
概念
闭包是指引用了另一个函数作用域中变量的函数,通常是在嵌套函数中实现的。闭包可以保留其被定义时的作用域,这意味着闭包内部可以访问外部函数的局部变量,即使外部函数已经执行完毕。这种特性使得闭包可以在后续调用中使用这些变量。即使创建函数的上下文已经销毁,函数和变量仍然存在,使用不当很容易导致内存泄漏。
var scope = "global scope";
function checkscope(){ //checkscopeContext.AO
var scope = "local scope";
function f(){
return scope;
}
return f;
}
var foo = checkscope();
foo();当 checkscope() 执行时,引擎为它的局部变量 scope="local scope" 创建一份活动对象(checkscopeContext.AO)。函数 f 在内部被创建,内部记录了对外层 checkscopeContext.AO 的引用([[Environment]] 指针)。 checkscope() 返回 f 并结束,正常情况下它的 checkscopeContext.AO 该被垃圾回收,但因为 f 仍拿着指向该 AO 的引用,引擎保留这份 checkscopeContext.AO——这就是闭包。变量 foo 拿到 f 后,在任何地方调用 foo(),都会沿着 f 保存的引用找到那份早已死亡的 AO,从而读出 "local scope"。就是因为这个作用域链,f 函数依然可以读取到 checkscopeContext.AO 的值。
说明当 f 函数引用了 checkscopeContext.AO 中的值的时候,即使 checkscopeContext 被销毁了,但是 JavaScript 依然会让 checkscopeContext.AO 活在内存中,f 函数依然可以通过 f 函数的作用域链找到它,正是因为 JavaScript 做到了这一点,从而实现了闭包这个概念
闭包与内存泄漏
闭包所保留下来的词法作用域,本质是由于外部自由变量的引用仍然可达,GC无法做到清理这块作用域链,虽然引用的变量可能很少,但是由于关联的词法作用域拥有的数量庞大,导致内存泄漏的风险扩大,不可控加剧。