你不知道的javascript--作用域闭包-1

2017-11-23  本文已影响0人  码畜小李哥

声明:以下内容摘自《你不知道的javascript》上卷一书的第5章的片段。

1.启示

对于那些有一点Javascript使用经验但从未真正理解闭包概念的人来说,理解闭包可以看作是某种意义上的重生,但是需要付出非常多的努力和牺牲才能理解这个概念。

Javascript中闭包无处不在,你只需要能够识别并拥抱它。

2.实质问题

【定义】当函数可以记住并访问所在的词法作用域时,就产生了闭包,即使函数是在当前词法作用域之外执行。

/*foo函数处于全局作用域中*/
function foo() {
    /*foo()块创建的作用域*/

    var a = 2;
    
    /*bar()块处于foo()块创建的作用域中,即bar()嵌套在foo()内部*/
    function bar() {
        /*
          bar()块创建的作用域
          bar()块具有一个覆盖foo()块作用域的闭包(事实上,涵盖了它能访问的所有作用域,比如全局作用域)
        */
        console.log( a );  // 2
    }
    
    bar();
}

foo();

基于词法作用域的查找规则,函数bar()可以访问外部作用域中的变量a (console.log( a )这里是一个RHS引用查询)。

这是闭包吗?

技术上来讲,也许是。但根据前面的定义,确切地说并不是
最准确地来解释bar()对a的引用的方法是词法作用域的查找规则,而这些规则只是闭包的一部分(但却是非常重要的一部分!)。
上面的代码定义的闭包并不能直接进行观察,也无法明白在这个代码片段中闭包是如何工作的。

下面这段代码,清晰地展示了闭包:

function foo() {

    var a = 2;
    
    function bar() {
        console.log( a );  // 2
    }
    
    return bar;
}

var baz = foo();
baz(); // 2 —— 朋友,这就是闭包的效果。

函数bar()的词法作用域能够访问foo()的内部作用域。然后我们将bar()函数本身当作一个值类型进行传递。

bar()显然可以被正常执行。但在这个例子中,它在自己定义的词法作用域意外的地方执行!

在foo()执行后,通常会期待foo()的整个内部作用域都被销毁,因为我们知道引擎有垃圾回收器来释放不再使用的内存空间。由于看上去foo()的内容不会再被使用,所以很自然地会考虑对其进行回收

而闭包的“神奇”之处正是可以阻止这件事情的发生。事实上内部作用域依然存在,因此没有被回收。谁在是哟和那个这个内部作用域?bar()本身!

拜bar()所声明的位置所赐,它拥有覆盖foo()内部作用域的闭包,使得该作用域能够一直存货,以供bar()在之后任何时候进行引用。

bar()依然持有对该作用域的引用,而这个引用就叫做闭包。

趁热打铁!再上两个代码例子!

function foo() {

    var a = 2;
    
    function baz() {
        console.log( a );  // 2
    }
    
    return bar( baz );
}

function bar(fn) {
    fn(); // 妈妈快看呀,这就是闭包!
}
var fn;

function foo() {

    var a = 2;
    
    function baz() {
        console.log( a );  
    }
    
    fn = baz; // 将baz分配给全局变量
}

function bar() {
    fn(); // 妈妈快看呀,这就是闭包!
}

foo();

bar(); // 2
上一篇 下一篇

猜你喜欢

热点阅读