首頁  >  文章  >  web前端  >  深入淺出JavaScript之閉包(Closure)的程式碼圖文詳細介紹

深入淺出JavaScript之閉包(Closure)的程式碼圖文詳細介紹

黄舟
黄舟原創
2017-03-03 15:37:161124瀏覽

閉包(closure)是掌握Javascript從人門到深入一個非常重要的門檻,它是Javascript語言的一個難點,也是它的特色,很多高級應用都要靠閉包實現。下面寫下我的學習筆記~

系列目錄

  • 深入淺出JavaScript之閉包(Closure)

  • 深入淺出JavaScript之this

  • 深入淺出JavaScript之原型鍊與繼承


閉包-無所不在


在前端程式設計中,使用閉包是非常常見的,我們常常有意無意,直接或間接用到了閉包。閉包可以讓傳遞資料更有彈性(例如處理一些點擊事件)

!function() {      
  var localData = "localData here";    
     document.addEventListener('click',    //处理点击事件时用到了外部局部变量,比如这里的localData       
        function(){              
           console.log(localData); 
    }); 
}();

又比如下面這個例子:(是不是很親切~~)

!function() {      
  var localData = "localData here";      
  var url = "http://www.baidu.com/";      
  $.ajax({ 
     url : url,          
     success : function() {              
        // do sth...              
        console.log(localData); 
        } 
    }); 
}();

再來看一個例子~~這種情況就是我們通常所說的閉包

function outer() {   
  var localVal = 30;    
  return function(){      
    return localVal;    
  } 
} 
var func = outer();  
func(); // 30

這個例子中調用outer()返回匿名函數function(),這個匿名函數中可以訪問outer()的局部變量localVal,在outer()調用結束後,再次呼叫func()的時候,仍然能訪問到outer()的局部變數localVal

#閉包的概念

閉包,不同於一般的函數,它允許一個函數在立即詞法作用域外調用時,仍可存取非本地變數。 –維基百科

閉包就是能夠讀取其他函數內部變數的函數。 –阮一峰

由於在Javascript語言中,只有函數內部的子函數才能讀取局部變量,因此可以把閉包簡單理解成」定義在一個函數內部的函數」。

所以,在本質上,閉包就是將函數內部和函數外部連接起來的一座橋樑

#閉包的用途

這部分轉自這篇博文

閉包可以用在許多地方。它的最大用處有兩個,一個是前面提到的可以讀取函數內部的變量,另一個就是讓這些變數的值始終保持在記憶體中。

function f1(){
    var n=999;
    nAdd=function(){n+=1}
    function f2(){
      alert(n);
    }
    return f2;
  }
  var result=f1();
  result(); // 999
  nAdd();
  result(); // 1000

在這段程式碼中,result其實就是閉包f2函數。它總共運行了兩次,第一次的值是999,第二次的值是1000。這證明了,函數f1中的局部變數n一直保存在記憶體中,並沒有在f1呼叫後被自動清除。

為什麼會這樣呢?原因就在於f1是f2的父函數,而f2被賦給了一個全域變量,這導致f2始終在記憶體中,而f2的存在依賴f1,因此f1也始終在記憶體中,不會在呼叫結束後,被垃圾回收機制(garbage collection)回收。

這段程式碼中另一個值得注意的地方,就是”nAdd=function(){n+=1}”這一行,首先在nAdd前面沒有使用var關鍵字,因此nAdd是一個全域變量,而不是局部變數。其次,nAdd的值是匿名函數(anonymous function),而這個匿名函數本身也是閉包,所以nAdd相當於一個setter,可以在函數外部對函數內部的局部變數進行運算。


閉包-封裝


#
(function() {   
   var _userId = 23492;   
   var _typeId = 'item';    
   var export = {}; 

   function converter(userId) {          
     return +userId; 
   } 
    export.getUserId = function() {         
       return converter(_userId);     
   } 
   export.getTypeId = function() {          
      return _typeId; 
   }         
   window.export = export;   //通过此方式输出
}());

  export.getUserId(); // 23492 
  export.getTypeId();  // item 
  export._userId;    // undefined  
  export._typeId;    // undefined       
  export.converter; // undefined

利用閉包的特性能讓我們封裝一些複雜的函數邏輯,在這個範例中呼叫export上的方法(getUserId,getTypeId)間接存取函數裡私有變量,但是直接呼叫export._userId是沒辦法拿到_userId的。這也是Node裡面常用到特性吧~


常見錯誤之循環閉包


##下面這個案例,我們新增3個p,值分別為aaa,bbb,ccc,我們想實現的是點選aaa輸出1,點選bbb輸出2,點選ccc輸出3

document.body.innerHTML = "<p id=p1>aaa</p>" + "<p id=p2>bbb</p><p id=p3>ccc</p>";  
for (var i = 1; i < 4; i++) {      
  document.getElementById(&#39;p&#39; + i).         
    addEventListener(&#39;click&#39;, function() {         
    alert(i); // all are 4! 
    });  
}

結果點選aaa,bbb或ccc都是alert(4)~~

產生這樣的問題在於這個

i的值在初始化完成的時候就已經是4了

要達到我們想要的點擊aaa輸出1,點擊bbb輸出2,點擊ccc輸出3,要用到閉包的技巧,在每次循環的時候,用立即執行的匿名函數把它包裝起來,這樣子做的話,每次alert(i)的值就取自閉包環境中的i,這個i來自每次循環的賦值i就能輸出1,2,3了

document.body.innerHTML = "<p id=p1>aaa</p>" + "<p id=p2>bbb</p>" + "<p id=p3>ccc</p>";  
for (var i = 1; i < 4; i++) {
  !function(i){ //②再用这个参数i,到getElementById()中引用     
    document.getElementById(&#39;p&#39; + i).       
      addEventListener(&#39;click&#39;, function() {         
      alert(i); // 1,2,3
     });  
  }(i);  //①把遍历的1,2,3的值传到匿名函数里面
}

思考題

如果你能理解下面兩段程式碼的運行結果,應該就算理解閉包的運作機制了。 (來自阮老師)這題目總結得真秒~~

程式碼片段一

var name = "The Window";
  var object = {
    name : "My Object",
    getNameFunc : function(){
      return function(){
        return this.name;
      };
    }
  };
  alert(object.getNameFunc()());

程式碼片段二

var name = "The Window";
  var object = {
    name : "My Object",
    getNameFunc : function(){
      var that = this;
      return function(){
        return that.name;
      };
    }
  };
  alert(object.getNameFunc()());

 以上就是深入淺出JavaScript之閉包(Closure)的程式碼圖文詳細介紹的內容,更多相關內容請關注PHP中文網(www.php.cn)!


#

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn