最近对JavaScript中的闭包做了一些深入的了解,目前的对闭包的理解也只能算是一知半解,现结合目前看过的资料,对闭包做一个初步的总结。
什么是闭包(Closures)
闭包是指有权访问另一个函数作用域中的变量的函数。
在函数的实际使用过程中,当函数被调用时,会创建函数执行环境及其作用域链,然后会根据arguments和其他命名参数初始化形成活动对象(AO)。这个执行环境仅在函数调用时存在,一旦函数执行完毕,该执行环境和作用域链则立即被销毁,有时候外部函数调用后,仍需要调用内部函数的活动对象,此时需要使用闭包。
内部函数
所谓内部函数,就是定义在一个函数之中的函数。
function outerFn () {
function innerFn () {}
}
innerFn ()是一个被包含在outerFn ()作用域中的内部函数,根据函数作用域链的查找方式,在outerFn ()内部调用innerFn ()是有效的,而在outerFn ()外部调用innerFn ()是无效的。
function outerFn() {
document.write("Outer function<br/>");
function innerFn() {
document.write("Inner function<br/>");
}
}
innerFn();//外部作用域链无法找到innerFn的作用域,执行产生错误
如果在outerFn()内部调用innerFn,则可以成功运行。
function outerFn() {
document.write("Outer function<br/>");
function innerFn() {
document.write("Inner function<br/>");
}
innerFn();
}
outerFn();
闭包使得内部函数可以在外部函数之外访问
闭包通常采用返回函数的方式,使外部函数的作用域保留在内部函数中,外部函数执行完毕后,其作用域链和活动对象依旧保留在内部函数中,故内部函数可以继续使用。
还是以上面的例子为例,要使innerFn()成为outerFn()的闭包,代码如下:
function outerFn() {
document.write("Outer function<br/>");
function innerFn() {
document.write("Inner function<br/>");
}
return innerFn;
}
var fnRef = outerFn();
fnRef();
在outerFn()内部,使用return innerFn;
语句作为函数的返回值,全局执行环境中,var fnRef = outerFn();
调用outerFn()函数,outerFn()函数会创建该函数的执行环境和活动变量,在outerFn()执行结束后,其作用域和活动变量理应被销毁,但是由于返回一个innerFn函数,其作用域和活动对象保留在innerFn函数中,使得在后续fnRef();
是可以访问到innerFn()。
在上面的例子中,我们称innerFn()
是outerFn()
的闭包。
总结
总的来说,闭包就是在函数中定义函数,闭包有权访问函数内部所有变量,闭包之所以能实现这些功能的原因如下:
1.后台执行环境中,闭包的作用域包含自身的作用域,包含函数的作用域和全局作用域。
2.通常,函数的作用域及其所有变量都会在函数执行结束后销毁。
3.但在函数返回一个闭包,这个函数的作用域会一直存在内存中,保存到闭包不存在为止。
更多相关概念
其实闭包需要涉及到的相关概念很多,包括执行环境/执行上下文(Execution context),变量对象(Variable Object),活动对象(Activation Object),作用域链(Scope chain),理解这些概念很更深层次的了解闭包的原理,这些会在后续博文继续总结。
参考资料
《JavaScript高级程序设计》
《jQuery基础教程》