闭包
来源:《JavaScript高级程序设计》
闭包是指有权访问另一个函数作用域中的变量的函数,创建闭包的常见方式就是在一个函数内部创建另一个函数。
如下面的例子:
function compare(prop){ return function(a, b){ var value1 = a[prop]; var value2 = b[prop]; return value1 - value2; }}
即使内部的匿名函数返回了,在其他地方被调用,它仍能够访问prop这个变量,直白来说是因为匿名函数的作用域链中包含了compare函数的作用域链。
想要了解背后的原理,先来看看函数被调用以后都发生了什么。
当函数被调用时,会创建一个执行环境(我理解为和作用域相近的概念,全局环境和局部环境)及相对应的作用域链(函数访问变量时会顺着这个链进行搜索同名变量)。然后使用arguments(实参)和其他命名参数(形参)的值来初始化函数的活动对象(这个变量对象是和当前执行环境是密不可分的)。
作用域链本质上是一个指向变量对象的指针列表,只引用但不实际包含变量对象。所以它的首项就是指向当前函数执行环境对应的那个变量对象的指针,然后第二项是外部函数的变量对象指针。。。末尾项则是指向了全局环境的顶层对象。顶层对象始终存在,但局部环境的变量对象会随着函数调用结束而被销毁掉。
下图展示了当下列代码执行时,包含函数与内部匿名函数的作用域链。
var comp = compare('name'); //这个时候comp这个变量引用了匿名函数var result = comp({name:'Nico'}, {name:'Greg'});
由于匿名函数的作用域链从里面的局部环境贯穿至全局环境,即使在compare函数执行完毕后,其活动对象也不会被销毁,因为匿名函数的作用域链仍然引用这个活动对象,直到匿名函数被销毁才会被销毁。
comp = null //解除对匿名函数的引用,释放内存
正因为闭包会导致它作用域链上的活动对象不会被垃圾回收机制销毁,因此更占内存。应慎重使用闭包。