对JavaScript闭包的理解

       闭包这个词其实很难准确的定义,个人在其他网站上看到关于它的定义挺挺不错的:内部函数总是可以访问其所在的外部函数中声明的参数和变量,即使在其外部函数被返回(寿命终结)了之后。

在正常情况下,一个函数访问完后,其内部定义的局部变量就会被销毁,例如:

    function fun() {
        var a = 'hello';
        b = 'world';
    }
    fun();
    console.log(b);//输出:world
    console.log(a);//输出:Uncaught ReferenceError: a is not defined

上面的例子中,变量a是一个局部变量,变量b是一个全局变量(非严格模式(不在第一行加'use strict')下,在函数内部不加var关键字定义变量时,自动声明为全局变量)。调用完fun函数后,b仍能输出fun中定义的值,a却不能,说明调用完fun函数后,变量a被销毁了,而变量b因为是全局变量,所以没有被销毁。

 

但是闭包不会出现上面那种情况,即使调用完毕,它内部定义的局部变量也不会被销毁,因为内部函数有外部函数变量的引用,例如:

    function fun() {
        var count = 0;
        function son() {
            count ++;
            console.log(count);
        }
        return son;
    }
    var C = fun();
    C();// 输出:1
    C();// 输出:2
    C();// 输出:3

上面的示例中,内部函数son引用了其外部函数的变量count,而外部函数直接将内部函数son返回了,这个时候内部函数son是没有执行的,正是因为还没有执行而又引用了外部函数的变量,导致外部函数中的变量无法销毁,从而一直存在内存中。

 

大多数情况下,闭包会与匿名函数结合在一起使用,例如:

    (function (fn) {
        fn();
    })(function () {
        var count = 0;
        var _gobal = {
            add : function () {
                ++count;
                console.log(count);
            }
        }
        window.gobal = _gobal;
    });
    gobal.add();//输出:1
    gobal.add();//输出:2
    gobal.add();//输出:3

上面的示例中,定义了一个匿名自执行函数,入参是一个匿名函数,即:

    function () {
        var count = 0;
        var _gobal = {
            add : function () {
                ++count;
                console.log(count);
            }
        }
        window.gobal = _gobal;
    }

在匿名自执行函数中调用了该匿名函数,这里没有像之前那样返回一个函数,但注意这一句“window.gobal = _gobal;”,它在 window 全局对象定义了一个变量 gobal,并将这个变量指向 _gobal对象,即全局变量 gobal引用了 _gobal. 而 _gobal对象中的函数又引用了匿名函数中的变量 count,因此匿名函数中的变量 count 不会被 GC 回收,count 会一直保存到内存中,所以这种写法满足了闭包的条件。

 

其实很多js插件都采用了闭包与匿名函数结合在一起使用的方式,例如jquery-1.12.4.js,下面是我精简后的代码:

    (function (global, factory) {
        if ( typeof module === "object" && typeof module.exports === "object" ) {
            //省略代码片段
        } else {
            factory( global );
        }
    }(typeof window !== "undefined" ? window : this, function(window, noGlobal) {
        //省略代码片段
        var
            version = "1.12.4",

            // Define a local copy of jQuery
            jQuery = function( selector, context ) {

                // The jQuery object is actually just the init constructor 'enhanced'
                // Need init if jQuery is called (just allow error to be thrown if not included)
                return new jQuery.fn.init( selector, context );
            },

            // Support: Android<4.1, IE<9
            // Make sure we trim BOM and NBSP
            rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,

            // Matches dashed string for camelizing
            rmsPrefix = /^-ms-/,
            rdashAlpha = /-([\da-z])/gi,

            // Used by jQuery.camelCase as callback to replace()
            fcamelCase = function( all, letter ) {
                return letter.toUpperCase();
            };

        //省略代码片段

        if ( !noGlobal ) {
            window.jQuery = window.$ = jQuery;
        }
    }));

注意到没有?它就是采用的这种方式,我们平常使用的$就是jQuery通过这种方式在匿名函数中暴露出来给我们使用的。

使用这种方式的好处就是避免全局污染,没有这种方式时,如果我想使得一些变量不在调用完之后被销毁,我可能要全部定义成全局变量,而这些变量又很容易被外部修改,采用这种闭包的方式后,我就可以将这些变量定义在函数内部却不在调用完后被销毁。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值