深入理解闭包

        这里的这个例子是引用于《JavaScript高级程序设计-第2版》的第4章-函数里的例子,结合对汤姆大叔博客里对函数的理解,需要对函数的构建及执行过程作以细致的分析理解,才能对闭包作以深入的分析与应用。以下是我对闭包的理解——

 1   (function($){
 2        
 3        function createFunction() {
 4            var result = new Array();
 5            for (var i = 0; i < 3; i++) {
 6                /**
 7                 * 当执行到下面这句代码的时候,每创建一个函数,就对应创建了一个对象,这个函数对象的[[scope]]属性里包括了该执行环境的VO和AO
 8                 * 而且这里的VO和AO充当了下面这个函数的公共作用域。
 9                 */
10                result[i] = function() {
11                    /**
12                     * 1>这个函数作用域可以访问另一个函数的作用域,那必然就是闭包了
13                     * 2>这个闭包返回了数组成员对象,会导致这里的i一直存在于内存中
14                     * 3>这里能够引出一个公共作用域的概念:
15                     *     a>这个函数的[[scope]] = [
16                     *                     createFunctionContext.AO,
17                     *                     匿名函数Context.AO,
18                     *                     globalContext.VO
19                     *                 ]
20                     *    b>当去检索i变量的时候,在第一层父级执行环境createFunctionContext.AO中就能够找到,而对于此时的i变量时一直存在于内存中的,
21              *      而且其值一直是3
22                     *    c>也就是说,这里的公共作用域里的变量正是这个函数的[[scope]](父级作用域链)属性里的值
23                     */
24                    return i;
25                }
26            }
27            return result;
28        }  
29        /**
30         * 当这个函数执行完成之后,这个函数的作用域链会被销毁,但是这个函数的活动对象并不销毁,仍然留在内存中,除非这里的funs变量被销毁,
31         * 才会释放这个函数里的i变量所占据的空间
32         */
33        var funs = createFunction();
34        for (var i = 0; i < funs.length; i++) {
35            document.write(funs[i]() + ',');  //3,3,3,
36        }
37        
38        //利用闭包保存状态
39        function createFunction() {
40            var result = new Array();
41            for (var i = 0; i < 3; i++) {
42                //第一种改造
43                (function(num) {  //每一次的循环都是一个函数的创建过程,当前这个函数的AO对象里面都保存了num变量
44                    /**
45                     * 为什么经过这个改造,它的打印结果就是0,1,2,了呢?一定要从作用域链的角度去理解,这样理解才是最为透彻的
46                     * 这个匿名函数的Scope = AO + [[scope]],随着循环的进行,相当于创建了三个这样的匿名函数,
47                     * 传入的这个num参数正是当前Scope里的AO里面的值,由于基本类型变量的传值是按值传递的,所以这里的num记录了
48                     * 每一次i的值并存入进当前AO里,因此当下面的这个匿名函数在执行时,访问的num值均是这里的AO对象里的num值。
49                     */
50                    result[num] = function() {  
51                        /**
52                         * 这里的num指代的是第一层closure里的num值,这里的num变量会一直存在于内存当中,
53                         * 除非重新赋值这里的result[num]为null,也就是说随着匿名函数的消失,它对应的作用域链和变量才会消失。
54                         */
55                        return num;  //
56                    }
57                    //尽管这里的num变量一直存在于内存当中,但这里的num变量仅仅是当前函数的局部变量,该匿名函数的外部是访问不到num的
58                })(i);  //当这里的匿名函数执行完成后,这里的num变量不会被立即被销毁,源于里面的匿名函数引用了num变量
59                
60                //第二种改造
61                result[i] = function(num) {  //和上面是一样的,每一次均创建函数并赋值给数组成员,对应的[[scope]]属性值相同,
62                                             //但是活动对象AO却不同,保存着不同的num值
63                    return function() {
64                        //当进入该执行环境时,返回的是不同的第一层closure的num值
65                        return num;  
66                    }
67                }(i);
68                
69            }  //当执行完这句代码的时候,这里的i变量就彻底没了
70            return result;
71        }
72    
73    })(jQuery);

        闭包是JavaScript函数应用的核心,深入理解了闭包,就能解释很多怪异的函数执行现象,当然,也能构造出非常巧妙的设计。

        还有一点要注意,之前在网上看多了很多有关闭包的说法,他们对闭包的理解真是五花八门,对此,我的建议是——一定要从作用域链(或者可以理解为标识符解析)的角度去思考理解闭包。

        注:随时欢迎任何博友对我的理解有误之处作以评论和更正。

posted @ 2012-08-10 16:45  金广国  阅读(358)  评论(0编辑  收藏  举报