您的位置:首页 > Web前端 > JavaScript

JavaScript中的闭包

2009-05-06 02:44 288 查看
闭包的特点与形式
闭包,作为一种特殊的结构,有其自身的特点和各种形式。
闭包的内在:自治的领域
闭包的“闭”是指闭包的内部环境对外部不可见,也就是说闭包具有控制外部域的能力但是又能防止外部域对闭包的反向控制。换句话说,闭包的领域是对外封闭的。
闭包的这一个特点不用过多解释,因为JavaScript闭包是通过function实现的,所以它天然具有基本的函数特征,在闭包内声明的变量,闭包外的任何环境中都无法访问的,除非闭包向外部环境提供了访问它们的接口。例如:
JavaScript之家欢迎你: http://www.jrose.cn/
闭包的封闭性
<html>
<head>
<title>Example闭包的封闭性</title>
</head>
<body>
<script>
<!--
function dwn(s)
{
document.write(s + "<br/>");
}
//我们说匿名函数调用产生一个“瞬时”的闭包
//因此当调用结束后,私有变量无法访问,并且如果没有外部引用存在
JavaScript之家欢迎你: http://www.jrose.cn/
//内部对象就会被销毁
//而如果返回了函数,或者被全局引用,则“闭包”被保留了下来
//闭包中的内容被“有选择”地开放出来
(function(){
//封闭的私有域
var innerX = 10, innerY = 20;
//开放的公共域
outerObj = {x : innerX, y : innerY} ;
})();

try{
dwn(innerX); //内部数据无法访问
}
catch(ex){
dwn("内部数据无法访问");
}
dwn(outerObj.x); //通过外部接口访问
-->
</script>
</body>
</html>

JavaScript之家欢迎你: http://www.jrose.cn/
访问外部环境
我们说,闭包可以访问外部环境,前面我们已经见过闭包读外部环境的例子,事实上闭包不但可以读外部环境,还可以写外部环境。
严格来说,外部环境既包括闭包外部的语法域也包括闭包外部的执行域。但是闭包对语法域环境的访问和普通函数一致,因此我们这里主要强调的是闭包对执行域环境的访问。
下面是一个用闭包写外部环境的例子:
闭包改变外部环境
<html>
<head>
<title>Example闭包改变外部环境</title>
</head>
<body>
<script>
<!—
//定义一个计数器生成函数,生成某种类型的计数器
function counter(iden, addi)
{
//闭包“外部”,函数counter“内部”的参数iden的值在闭包被调用的时候会被改变
return function(){
//改变iden的值
iden = iden+addi;
return iden;
}
}
//产程一个从0开始计数,每次计数值加1的计数器
var c1 = counter(0, 1);
//产生一个从10开始计数,每次计数值减1的计数器
var c2 = counter(10, -1);
for(var i = 0; i < 10; i++){
//循环计数
c1();
}
for(var i = 0; i < 10; i++){
//循环计数
c2();
}
-->
</script>
</body>
</html>
JavaScript之家欢迎你: http://www.jrose.cn/
我们说c1和c2通过调用counter构造了两个不同的计数器它们的初值分别是0和10,步长分别是1和-1,在调用闭包时,我们用步长改变计数器值iden,使得计数器的值按照给定的步长递增。
上面的例子用面向对象的思想也能够实现,但是用闭包从形式上要比用对象简洁一些,后面我们会看到,实际上我们在上面的例子中用了另外一种和面向对象等同的抽象思想,即函数式(functional)思想。
有趣的是,外部环境的读写和闭包出现在函数体内的顺序没有关系,例如:
function createClosure(){
var x = 10;
return function()
{
return x;
}
}

function createClosure(){
function a()
{
return x;
}
var x = 10;
return a;
}
的结果是一样的。
闭包和面向对象
我们说,JavaScript的对象中的私有属性其实就是环境中的非持久型变量,而在构造函数内用this.foo = function(){…}形式定义的方法其实也是闭包的一种创建形式,只是它提供的是一种开放了“外部接口”的闭包:
闭包和面向对象
<html>
<head>
<title>Example 闭包和面向对象</title>
</head>
<body>
<script>
<!--
function dwn(s)
{
document.write(s + "<br/>");
}
//定义一个Foo类型
function Foo(a)
{
function _pC() //私有的函数
{
return a;
}
//公有的函数,通过它产生的闭包可以访问对象内部的私有方法_pC()
this.bar = function(){
dwn("foo" + _pC() + "!");
}
}
var obj = new Foo("bar");
obj.bar(); //显示Foo bar!
-->
</script>
</body>
</html>

其他形式的闭包
JavaScript的闭包不仅仅只有以上几种简单的形式,它还有其他更加“诡异”的形式,例如:
闭包的其他形式
<html>
<head>
<title>Example 闭包的其他形式</title>
</head>
<body>
<script>
<!—
//测试函数,异步计数
function test()
{
for (var i = 0; i < 5; i++)
{
//如果没有这个闭包,不能正确得到0,1,2,3,4的结果
//因为setTimeout是在循环结束后才被“异步”调用的
(function(j){
setTimeout(function(){alert(j)}, 100);
})(i);
}
}
test();
-->
</script>
</body>
</html>
JavaScript之家欢迎你: http://www.jrose.cn/
这个例子我们曾经见到过,function(j){setTimeout(function(){alert(j)}, 100);}是一个闭包,它访问test()的调用环境,而function(){alert(j)}也是一个闭包,它又访问由外部闭包提供的环境。这样的闭包使用法经常被用在异步的环境中,用来将特定的引用“绑定”给闭包。例如,下面的用法通过闭包环境绑定修正了事件注册时的“this”指针:
button1.onclick =
(function(owner){return function(){button1_click.apply(owner,arguments)}})(button1);
JavaScript之家欢迎你: http://www.jrose.cn/
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: