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特殊的作用域鏈,閉包會在父函數外部,改變父函數內部變量的值。

 

發佈了19 篇原創文章 · 獲贊 3 · 訪問量 4萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章