1. 程式人生 > >javascript中的返回函式與閉包

javascript中的返回函式與閉包

Javascript中沒有類的概念,函式就是第一類物件。
函式就是物件,主要的表現形式有:
1. 函式可以在執行時建立,也可以在執行的過程中建立。
2. 函式可以被分配給其他變數,可以將它們的引用複製給其他變數。
3. 函式可以作為引數傳遞給其他函式,可以作為其他函式的返回值返回。
4. 函式可以有自己的屬性和方法。

本文將重點討論函式作為返回值的形式。

例如:
 var setup = function(){ 
   //
函式setup的私有變數 
   var count = 0; 
   //
返回函式訪問私有變數 
   return function(){ 
     return (count += 1); 
   }; 
 };


//外部呼叫返回函式 
 var next = setup(); 
 alert(next()); //1 
 alert(next()); //2 
 alert(next()); //3 
 alert(next()); //4 

通過以上程式碼,可以看到2個現象:
1. 變數count是函式setup的私有變數,外部函式是無法直接訪問的,但是我們可以通過函式setup的內部匿名函式訪問。如果我們將這個內部函式返回,外部呼叫該函式的時候就可以間接訪問函式setup的私有變數。

2. 函式setup的私有變數count看起來像一個靜態變數,每次呼叫都可以在上一次呼叫的基礎上遞增

1

現象1
Javascript中,函式有兩個特別的特徵,一是前面說過的函式就是物件,二是函式提供區域性作用域。這和Java{}提供變數作用域是有區別的。
Javascript中的作用域鏈訪問模式: 
<script>
 //
全域性作用域 
 var global = "global"; 

 function outer() 
 { 
   //
函式outer的作用域 
   var outer_v = "outer"; 
   alert(global); //global
,能訪問全域性作用域的變數 
   //alert(inner_v); //

不能訪問內部函式作用域的變數 

   var inner = function(){ 
    //
函式inner的作用域 
    var inner_v = "inner"; 
    alert(outer_v); //outer
,能訪問外部函式作用域的變數 
   } 
   inner();
 } 
  outer();
  //alert(outer_v); // 
不能訪問oute函式作用域的變數。
</script>

以上程式碼就是為了說明Javascript語言特有的鏈式作用域結構(chain scope)。即子物件會一級一級地向上尋找所有父物件的變數。所以,父物件的所有變數,對子物件都是可見的,反之則不成立。

現象2
為什麼函式setup的私有變數count的表現好似Java中的靜態變數?
  var next = setup(); 

   
不難理解,這句話呼叫之後,我們建立了一個全域性變數next指向了函式setup的內部函式,所以setup的內部函式將一直存在於記憶體中,不會被垃圾回收器回收。而內部函式的存在是依賴外部函式setup的,所以setup也會一直存在於記憶體中而不被銷燬。所以其私有屬性的值不會被重置。特別注意,Javascript中函式不是類,是第一類物件,歸根結底是物件,相當於記憶體中存在一個不被銷燬的物件,所以該物件的屬性不會被改變,這和Java中的靜態變數是有區別的。

可以看出,隨意使用返回函式是很消耗效能的,因為這些函式物件將一直存在於記憶體中。其實以上闡述的這種返回函式的模式,就是Javascript中所謂的閉包。

閉包的概念
官方的解釋是:閉包是一個擁有許多變數和綁定了這些變數的環境的表示式(通常是一個函式),因而這些變數也是該表示式的一部分。
我的理解是,閉包就是在函式外部使用函式內部的返回函式。也就是說:當函式a的內部函式b被函式a外的一個變數引用的時候,就建立了一個閉包。

閉包的作用主要就是為了保護私有變數。使用閉包的注意事項:
  1.由於閉包會使得函式中的變數都被儲存在記憶體中,記憶體消耗很大,所以不能濫用閉包,否則會造成網頁的效能問題,在IE中可能導致記憶體洩露。
  2.由於Javascript特殊的作用域鏈,閉包會在父函式外部,改變父函式內部變數的值。