javascript的閉包

今天又在無憂看到閉包的使用了,整理一下閉包的東西。

閉包的定義非常晦澀——閉包,是指語法域位於某個特定的區域,具有持續參照(讀寫)位於該區域內自身範圍之外的執行域上的非持久型變量值能力的段落。這些外部執行域的非持久型變量神奇地保留它們在閉包最初定義(或創建)時的值(深連結)。簡單來說,閉包就是在另一個作用域中保存了一份它從上一級函數或作用域取得的變量(鍵值對),而這些鍵值對是不會隨上一級函數的執行完成而銷燬。周愛民說得更清楚,閉包就是“屬性表”,閉包就是一個數據塊,閉包就是一個存放着“Name=Value”的對照表。就這麼簡單。但是,必須強調,閉包是一個運行期概念。

在Javascript中閉包(Closure),有兩個特點:

  • 作爲一個函數變量的一個引用 - 當函數返回時,其處於激活狀態。
  • 一個閉包就是當一個函數返回時,一個沒有釋放資源的棧區。

現在比較讓人認同的閉包實現有如下三種

with(obj){
    //這裏是對象閉包
    }
(function(){
    //函數閉包
    })()
try{
//...
} catch(e) {
//catch閉包 但IE裏不行
}

幾個有用的示例

//*************閉包uniqueID*************
uniqueID = (function(){ //這個函數的調用對象保存值
    var id = 0; //這是私有恆久的那個值
    //外層函數返回一個有權訪問恆久值的嵌套的函數
    //那就是我們保存在變量uniqueID裏的嵌套函數.
    return function(){return id++;};  //返回,自加.
})(); //在定義後調用外層函數.
document.writeln(uniqueID()); //0
document.writeln(uniqueID()); //1
document.writeln(uniqueID()); //2
document.writeln(uniqueID()); //3
document.writeln(uniqueID()); //4
//*************閉包階乘*************
var a = (function(n){
    if(n<1){ alert("invalid arguments"); return 0; }
    if(n==1){ return 1; }
    else{ return n * arguments.callee(n-1); }
})(4);
document.writeln(a);
function User( properties ) {   
    //這裏一定要聲明一個變量來指向當前的instance   
    var objthis = this;   
    for ( var i in properties ) {   
        (function(){   
                //在閉包內,t每次都是新的,而 properties[i] 的值是for裏面的   
                var t = properties[i];   
                objthis[ "get" + i ] = function() {return t;};   
                objthis[ "set" + i ] = function(val) {t = val;};   
        })();    
    }   
}   
     
//測試代碼   
var user = new User({   
    name: "Bob",   
    age: 44   
});   
     
alert( user.getname());   
alert( user.getage());   
     
user.setname("Mike");   
alert( user.getname());   
alert( user.getage());   
     
user.setage( 22 );   
alert( user.getname());   
alert( user.getage());  

附上今天在無憂看到的問題:

要求:

讓這三個節點的Onclick事件都能正確的彈出相應的參數。

<ul>
   <li id="a1">aa</li>
   <li id="a2">aa</li>
   <li id="a3">aa</li>
</ul>
 
<script type="text/javascript">
 
for(var i=1; i < 4; i++){
   var id = document.getElementById("a" + i);
   id.onclick = function(){
        alert(i);//現在都是返回4
   }
}
</script>

我的解釋是,onclick的綁定函數 function(){alert(i)}的作用域爲對應li對象,它裏面alert的i的作用域爲window,每次循環都是在重寫window.i的值,因此循環完,i已是4,點擊哪一個li元素都是4。

解決方法:

  1. 使用函數閉包。

    var lists = document.getElementsByTagName("li");
    for(var i=0,l=lists.length; i < l; i++){
      lists[i].onclick = (function(i){//保存於外部函函數
        return function(){
          alert(i);
        }
      })(i);
    }
    var lists = document.getElementsByTagName("li");
    for(var i=0,l=lists.length; i < l; i++){
      lists[i].onclick = new function(){
        var t = i;
        return function(){
          alert(t+1)
        }
      }
    }
  2. 利用事件代理

    var ul = document.getElementsByTagName("ul")[0];
    ul.onclick = function(){
      var e = arguments[0] || window.event,
      target = e.srcElement ? e.srcElement : e.target;
      if(target.nodeName.toLowerCase() == "li"){
        alert(target.id.slice(-1))
      }
    }
  3. 將暫時變量保留於元素節點上。

    var lists = document.getElementsByTagName("li");
    for(var i=0,t=0,el; el = list[i++];){
      el.i = t++
      el.onclick = function(){
        alert(this.i)
      }
    }
  4. 使用with語句造成的對象閉包。

    var els = document.getElementsByTagName("li")
         for(var i=0,n=els.length;i<n;i++){
           with ({i:i})
           els[i].onclick = function() { alert(this.innerHTML+i) };
         }
  5. 使用try...catch語句構造的異常閉包:

    var lists = document.getElementsByTagName("li");
    for(var i=0,l=lists.length; i < l; i++){
      try{
        throw i;
      }catch(i){
        lists[i].onclick =  function(){
          alert(i)
        }
      }
    }
var els = document.getElementsByTagName("li");
 
(''+Array(els.length+1)).replace(/./g,function(a,i){
  els[i].onclick=function(){alert(i)}
})
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章