JavaScript 閉包LOGIN

JavaScript 閉包

JavaScript 閉包

什麼是閉包

閉包,官方對閉包的解釋是:一個擁有許多變數和綁定了這些變數的環境的表達式(通常是一個函數),因而這些變數也是該表達式的一部分。閉包的特點:

  1. 作為一個函數變數的一個引用,當函數傳回時,其處於激活狀態。

  2. 一個閉包就是當一個函數回傳時,一個沒有釋放資源的堆疊區。

  簡單的說,Javascript允許使用內部函數---即函數定義和函數表達式位於另一個函數的函數體內。而且,這些內部函數可以存取它們所在的外部函數中聲明的所有局部變數、參數和聲明的其他內部函數。當其中一個這樣的內部函數在包含它們的外部函數之外被呼叫時,就會形成閉包。

2、閉包的幾種寫法和用法

首先要明白,在JS中一切都是對象,函數是對象的一種。下面先來看看閉包的5種寫法,簡單理解一下什麼是閉包。後面會具體解釋。

//第1種寫法  

function Circle(r) {  
      this.r = r;  
}  
Circle.PI = 3.14159;  
Circle.prototype.area = function() {  
  return Circle.PI * this.r * this.r;  
}  
var c = new Circle(1.0);     
alert(c.area());

這種寫法沒什麼特別的,只是為函數增加一些屬性。

//第2種寫法  

var Circle = function() {  
   var obj = new Object();  
   obj.PI = 3.14159;       
   obj.area = function( r ) {  
       return this.PI * r * r;  
   }  
   return obj;  
}    
var c = new Circle();  
alert( c.area( 1.0 ) );

這種寫法是宣告一個變量,將一個函數當作值賦給變數。

//第3種寫法  

var Circle = new Object();  
Circle.PI = 3.14159;  
Circle.Area = function( r ) {  
       return this.PI * r * r;  
}  
alert( Circle.Area( 1.0 ) );

這種方法最好理解,就是new 一個對象,然後給對象添加屬性和方法。

//第4種寫法  

var Circle={  
   "PI":3.14159,  
 "area":function(r){  
          return this.PI * r * r;  
        }  
};  
alert( Circle.area(1.0) );

這種方法使用較多,也最為方便。 var obj = {}就是宣告一個空的物件。

//第5種寫法 

var Circle = new Function("this.PI = 3.14159;this.area = function( r ) {return r*r*this.PI;} ");    

alert( (new Circle()).area(1.0) );  

說實話,這種寫法我是沒用過,大家可以參考一下。  

總的來說,上面幾種方法,第2中和第4中較為常見,大家可以依照習慣選擇。  

上面程式碼中出現了JS中常用的Prototype,那麼Prototype有什麼用呢?下面我們來看看:

  var dom = function(){    
    };
    dom.Show = function(){
        alert("Show Message");
    }; 
    dom.prototype.Display = function(){
        alert("Property Message");
    };
    dom.Display(); //error
    dom.Show();  
    var d = new dom();
    d.Display();
    d.Show(); //error

變數生命週期

全域變數的作用域是全域性的,也就是在整個JavaScript程式中,全域變數處處都在。

而在函數內部宣告的變量,只在函數內部運作。這些變數是局部變量,作用域是局部性的;函數的參數也是局部性的,只在函數內部起作用。

計數器困境

設想下如果你想統計一些數值,且該計數器在所有函數中都是可用的。

你可以使用全域變量,函數設定計數器遞增:

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
</head>
<body>
<p>全局变量计数。</p>
<button type="button" onclick="myFunction()">计数!</button>
<p id="demo">0</p>
<script>
var counter = 0;
function add() {
    return counter += 1;
}
function myFunction(){
    document.getElementById("demo").innerHTML = add();
}
</script>
</body>
</html>

計數器數值在執行 add() 函數時會改變。

但問題來了,頁面上的任何腳本都能改變計數器,即使沒有呼叫 add() 函數。

如果我在函數內宣告計數器,如果沒有呼叫函數將無法修改計數器的值:

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
</head>
<body>
<p>局部变量计数。</p>
<button type="button" onclick="myFunction()">计数!</button>
<p id="demo">0</p>
<script>
function add() {
    var counter = 0;
    return counter += 1;
}
function myFunction(){
    document.getElementById("demo").innerHTML = add();
}
</script>
</body>
</html>

以上程式碼將無法正確輸出,每次我呼叫add() 函數,計數器都會設定為1。

JavaScript 內嵌函數可以解決這個問題。

JavaScript 內嵌函數

所有函數都能存取全域變數。  

實際上,在 JavaScript 中,所有函數都能存取它們上一層的作用域。

JavaScript 支援巢狀函數。巢狀函數可以存取上一層的函數變數。

該實例中,內嵌函數 plus() 可以存取父函數的 counter 變數:

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
</head>
<body>
<p>局部变量计数。</p>
<p id="demo">0</p>
<script>
document.getElementById("demo").innerHTML = add();
function add() {
var counter = 0;
    function plus() {counter += 1;}
    plus();    
    return counter; 
}
</script>
</body>
</html>

如果我們能在外在中存取 plus() 函數,這樣就能解決計數器的兩難。

我們同樣需要確保 counter = 0 只執行一次。

我們需要閉包。


下一節
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> </head> <body> <p>局部变量计数。</p> <button type="button" onclick="myFunction()">计数!</button> <p id="demo">0</p> <script> var add = (function () { var counter = 0; return function () {return counter += 1;} })(); function myFunction(){ document.getElementById("demo").innerHTML = add(); } </script> </body> </html>
章節課件