JavaScript闭包

1、内部函数:所谓内部函数,就是定义在另一个函数中的函数。

function outerFun(){

    function innerFun(){ alert("hihi");}

}

innerFun()就是一个被包含在outerFun()作用域中的内部函数。这意味着,在outerFun()内部调用innerFun()是有效的,而在outerFun()外部调用innerFun()则是无效的。

不过可以在outerFun()内部调用innerFun():

function outerFun (){

    function innerFun() { alert("hihi");}

    innerFun();

}

 

outerFun();

这种技术特别适合于小型、单用途的函数。例如,递归但却带有非递归API包装的算法通常最适合通过内部函数来表达。

 

2、伟大的逃逸

Javascript允许像传递任何类型的数据一样传递函数。也就是说,JavaScript中的内部函数能够逃脱定义在它们的外部函数。

逃逸方式一:

var globVar;

 

function  outerFun(){

    function innerFun(){alert("hihi");}

    globVar = innerFun;

}

outerFun();

globVar();

 

逃逸方式二:

function  outerFun(){

    function innerFun(){alert("hihi");}

    return innerFun;

}

var globVar = outerFun();

globVar();

从 outerFun ()中返回一个对innerFun()的引用,通过outerFun() 能够取得这个引用,而且,这个引用可以保存在变量中,也可以自己调用自己。

这种即使在离开函数作用域的情况下,仍然能够通过引用调用内部函数的事实,意味着只要存在调用这些内部函数的可能,JavaScript就需要保留被引用的函数。而且,JavaScript运行时程序需要跟踪引用这个内部函数的所有变量,直至最后一个变量废弃,JavaScript的垃圾收集器才能出面释放相应的内存空间。

 

3、变量作用域

内部函数当然也可以拥有自己的变量,只不过这些变量都被限制在内部函数的作用域中:

function outerFun (){

    function innerFun(){
        var innerVar = 0;
        innerVar ++;
        alert(innerVar);
    }
    return innerFun;   
}
每当通过引用或其他方式调用这个内部函数时,都会创建一个新的innerVar变量,然后递增,最后显示:
var globVar = outerFun();
globVar();//警告框显示:1
globVar();//警告框显示:1
var globVar2 = outerFun();
globVar2();//警告框显示:1
globVar2();//警告框显示:1

----
内部函数可以像其他函数一样引用全局变量:
var globVar = 0;
function outerFun (){

    function innerFun(){
        globVar ++;
        alert(globVar);
    }
    return innerFun;   
}
每当通过引用或其他方式调用这个内部函数时,都会创建一个新的innerVar变量,然后递增,最后显示:
var globVar = outerFun();
globVar();//警告框显示:1
globVar();//警告框显示:2
var globVar2 = outerFun();
globVar2();//警告框显示:3
globVar2();//警告框显示:4
但是,如果这个变量是父函数的局部变量又会怎样呢?因为内部函数会继承父函数的作用域,所以内部函数也可以引用这个变量:
function outerFun (){
    var outerVar = 0;
    function innerFun(){
        outerVar ++;
        alert(outerVar);
    }
    return innerFun;   
}
这一次,对内部函数的调用会产生有意思的行为:
var globVar = outerFun();
globVar();//警告框显示:1
globVar();//警告框显示:2
var globVar2 = outerFun();
globVar2();//警告框显示:1
globVar2();//警告框显示:2
通过每个引用调用innerFun() 都会独立地递增innervar。也就是说,第二次调用outerFun()没有继承沿用innerVar的值,而是在第二次函数调用的作用域中创建并绑定一个新的innerVar的实例。结果,就造成了在上面的调用之后继续调用globVar()会导致警告框显示3,而再次调用globVar2()也会导致警告框显示3。这两个计数器完全是无关的。
当内部函数在定义它的作用域的外部被引用时,就创建了该内部函数的一个“闭包”。在这种情况下,我们称不是内部函数局部变量的变量为“自由变量”,称外部函数的调用环境为“封闭”闭包的环境。从本质上讲,如果内部函数引用了位于外部函数的变量,相当于授权该变量能够被延迟使用。因此,当外部函数调用完成后,这些变量的内存不会被释放,因为闭包仍然需要使用它们。

4、闭包之间的交互
当存在多个内部函数时,很可能出现意料之外的闭包。假设我们又定义一个递增函数,这个函数中的增量为2:
function outerFun (){
    var outerVar = 0;
    function innerFun(){
        outerVar++;
        alert(outerVar);
    }
    function innerFun2(){
        outerVar = outerVar + 2;
        alert(outerVar);
    }
    return {'innerFun': innerFun, 'innerFun2':innerFun2};
}
这里,我们通过映射返回两个内部函数的引用(这也示范了内部函数的引用逃脱父函数的另一种方式)。可以通过返回的引用调用任何一个内部函数:
var globVar = outerFun();
globVar.innerFun();//警告框显示:1
globVar.innerFun2();//警告框显示:3
globVar.innerFun();//警告框显示:4
var globVar2 = outerFun();
globVar2.innerFun();//警告框显示:1
globVar2.innerFun2();//警告框显示:3
globVar2.innerFun();//警告框显示:4
这两个内部函数引用了同一个局部变量,因此它们共享同一个封闭环境。当innerFun()为outerVar递增1时,就为调用innerFun2()设置了outerVar的新的起点。同样,我们也看到对outerFun()的后溪调用还会创建这些闭包的新实例,同时也会创建相应的新封闭环境。面向对象编程的爱好者们会注意到,这在本质上是创建了一个新对象,自由变量就是这个对象的实例变量,而闭包就是这个对象的实例方法。而且,这些变量也是私有的,因为不能在封装它们的作用域外部直接引用这些变量,从而确保了面向对象的数据专有特性。

——《jQuery基础教程》附录3

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值