javascript闭包

  只有函数作用域的好处是内部函数可以访问定义它们的外部函数的参数和变量(除了this和arguments)。这是一件非常好的事情。 
我们的getElementsByAttribute函数可以工作是因为它声明了一个results变量,且传递给walk_the_DOM的内部函数也可以访问results变量。 
一个更有趣的情形是内部函数拥有比它的外部函数更长的生命周期。 
之前,我们构造了一个myObject对象,它拥有一个value属性和一个increment方法。假定我们希望保护该值不会被非法更改。 

与前面直接定义一个对象不同,我们通过调用一个函数的形式去初始化myObject,该函数将返回一个对象。此函数定义了一个value变量。该变量对increment和getValue方法总是可见的,但函数的作用域使得它对其他的程序来说是不可见的。

var myObject=function(){
       var value=0;
       return {
              increment:function(inc){
                     value+=typeof inc==='number'?inc:1;
              },
              getValue:function(){
                     return value;
              }
       }
}();

我们并没有把一个函数赋值给myObject,我们是把调用该函数后返回的结果赋值给它(默然说话:注意最后一行的())。该函数返回一个包含两个方法的对象,并且这些方法继续享有访问value变量的特权。
本章之前的Quo构造器产生出带有status属性和get_status方法的一个对象。但那看起来并不是十分有趣。为什么要用一个 getter方法去访问本可以直接访问到的属性呢?如果status是私有属性时,它才是更有意义的。所以,让我们定义另一种形式的quo函数来做此事:

//创建一个名为quo的构造函数。
//它构造出带有get_status方法和status私有属性的一个对象。
var quo=function(status){
       return {
              get_status:function(){
                     return status;
              },
              set_status:function(st){
                     status=st;
              }
       };
};
//构造一个quo实例
var myQuo=quo(“amazed”);
document.writeln(myQuo.get_status());
这个quo函数被设计成无须在前面加上new来使用,所以名字也没有首字母大写(默然说话:当然,你也可以加new,效果是一样的)。 当我们调用quo时,它返回包含get_status方法的一个新对象。该对象的一个引用保存在myQuo中。即使quo函数已经运行结束,但 get_status方法仍然享有访问status的特权。get_status方法并不是访问该参数的一个拷贝,它访问的就是该参数本身。因为该函数可 以访问它被创建时所处的上下文环境。这就被称为闭包。

//定义一个函数,它设置一个DOM节点为黄色,然后把它渐变为白色
var fade=function(node){
       var level=1;
       var step=function(){
              var hex=level.toString(16);
              node.style.backgroundColor='#FFFF' +hex+hex;
              if(level<15){
                     level+=1;
                     setTimeout(step,100);
              }
       };
       step();
};
<body οnlοad=”fade(document.body)”></body>
我们调用fade,把document.body作为参数传递给它(HTML<body>标签所创建的节点).fade函数设置level为1。它定义了一个step函数;接着调用step函数,fade函数结束。
step函数把fade函数的level变量转化为16进制字符。接着,它修改fade函数得到的节点的背景色。然后查看fade函数的level变量。如果背景还没变成白色,那就增大level变量再使用setTimeout让自己再次运行。
step很快被再次调用,这时fade函数早已运行结束,但只要fade的内部函数需要,它的变量就会保留(默然说话:耶!伟大的闭包!!!)。
理解内部函数能访问外部函数的实际变量本身而不是一个副本非常重要,看下面的例子。
//糟糕的例子
//构造一个函数,用错误的方式给一个数组中的节点设置事件处理程序。
//当点击一个节点时,按照预想应该弹出一个对话框显示节点的序号
//但其实所有的事件总是会显示节点的数目。
var add_the_handlers=function(nodes){
       var i;
       for(i=0;i<nodes.length;i++){
              nodes[i].οnclick=function(e){
                     alert(i);//因为这里是直接引用了变量i,而不是副本,所以当点击节点时,总是显示循环之后i的值
              }
       }
}
<body οnlοad="add_the_handlers(document.getElementsByTagName('div'))">
<div style="width:300px;height:300px;border:1px solid black;"></div>
<div style="width:300px;height:300px;border:1px solid black;"></div>
<div style="width:300px;height:300px;border:1px solid black;"></div>
<div style="width:300px;height:300px;border:1px solid black;"></div>
</body>
add_the_handlers函数目的是给每个事件处理函数一个唯一值(默然说话:即每一次循环时i的值,它需要很多个i的副本,每个i值都不一样),但它直接引用了i,所以每个事件处理函数都得到了循环后i最终的值。
//好例子
//构造一个函数,用正确的方式给一个数组中的节点设置事件处理程序。
//你点击一个节点,将会弹出不同的序号
var add_the_handlers=function(nodes){
       var i;
       for(i=0;i<nodes.length;i++){
              nodes[i].οnclick=function(e){
                     return function(){
                            alert(e);
                     };
              }(i);
       }
};
 
<body οnlοad=" add_the_handlers(document.getElementsByTagName('div'))">
<div style="width:300px;height:300px;border:1px solid black;"></div>
<div style="width:300px;height:300px;border:1px solid black;"></div>
<div style="width:300px;height:300px;border:1px solid black;"></div>
<div style="width:300px;height:300px;border:1px solid black;"></div>
</body>
</html>
现在,我们定义了一个函数并立即传递i进去执行,而不是把一个函数赋值给onclick。那个函数将返回一个事件处理函数。这个事件处理函数打印的是e而不是i,这样就可以避免以上情况.

来自《javascript语言精粹》

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值