JavaScript 闭包(一)

Posted by LiJiaHao' Blog on September 15, 2015

最近对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 闭包究竟是什么

《JavaScript高级程序设计》

《jQuery基础教程》


原创不易,如果觉得这篇文章对你有帮助,不如赏杯咖啡吧
微信
支付宝