JavaScript學習筆記之函數表達式與閉包

1.函數表達式與函數聲明

在JavaScript中,定義函數有兩種方式,一種是函數聲明,一種是函數表達式。

函數聲明的語法如下:

function functionName(arg0,arg1){
     //函數體
}
函數表達式的語法如下:

var functionName=function(arg0,arg1){
      //函數體
};
這兩種方法有着一下的區別:

第一、函數聲明有一個重要特徵就是函數聲明提升(function declaration hoisting),就是說在代碼執行之前會先讀取函數聲明,因此可以將執行放在函數聲明的前面。而函數表達式不具有這個特徵。如對於以下代碼:

sayHi();
function sayHi(){
      alert("Hi!");
}
上面這個例子採用的是函數聲明,所以不會有問題。

sayHi();  //錯誤:函數還不存在
var sayHi=function(){
      alert("Hi!");
};
而這個例子由於採用的是函數表達式,無法再執行時讀取函數聲明,所以會出現錯誤。

第二、函數表達式可以像普通變量一樣作爲函數的結果返回,因此函數表達式可以用來實現遞歸,構造閉包,而函數聲明不可以。
2.閉包以及閉包的幾個用途

首先搞清閉包的概念:閉包是指有權訪問另一個函數作用域中的變量的函數。創建閉包的常見方式,就是在一個函數內部創建另一個函數。

要想很好的理解閉包的概念,首先來看下下面的這個例子。

function outer(argOut){
      return function(argIn1,argIn2){
            return argOut+argIn1+argIn2;
      };
}

var addWithOuter=outer("a");
var result=addWithOuter("b","c");    //result爲"abc"

var addWithOuter=null;
在上面的例子中,outer函數內部聲明瞭一個匿名函數,根據JavaScript的作用域鏈機制(如果對這個概念不瞭解,請戳JavaScript學習筆記之作用域鏈),內部匿名函數可以訪問外部函數的變量argOut,也就是說它可以訪問另一個函數(outer)的作用域中的變量,因此就構成了一個閉包。但是構成了閉包又怎麼樣呢?沒什麼大驚小怪的啊好像。。。。咳咳,下面重點來了,當調用outer函數,並將結果函數返回給變量addWithOuter時,按照正常的邏輯,這個函數調用完之後,它的活動對象包括函數內的一些變量應該被銷燬,也就是說這裏的“a”應該就被銷燬了,但是,有了閉包情況就不同了。我們可以看到,在後面繼續調用addWithOuter指向的函數(因爲outer返回給它的就是一個函數)時,依舊能訪問變量“a”並返回正確的結果“abc”,也就是說對於閉包的情況,外層函數的活動對象沒有隨着函數調用結束而銷燬。這是爲什麼呢?

根據JavaScript的作用域鏈機制,匿名函數的作用域鏈引用着外層函數的活動對象,因此當外層函數調用結束時,由於匿名函數還存在(被返回給了一個變量),而且其作用域鏈還在引用着外層函數的活動對象,因此外層函數的活動對象不會銷燬,只會銷燬外層函數的作用域鏈(指向活動對象的指針鏈)。直到解除addWithOuter的引用,纔會銷燬匿名函數的作用域鏈,外層的活動對象也就隨着銷燬了。

閉包的這種作用域鏈機制會佔用比較多的內存,可能會導致IE9之前版本的IE瀏覽器發生內存泄露的問題。此外這種機制也決定了閉包只能取得包含函數中任何變量的最後一個值。

那麼閉包有什麼用途呢?

第一、閉包可以模仿塊級作用域

(functon(){
      //這裏是塊級作用域
})();
注意上面最後的“()”,表示函數直接執行,而塊級作用域裏的變量會隨着匿名函數的執行完畢而立即銷燬。

第二、閉包可以用於在對象中構造私有變量

function Person(name){
      this.getName=function(){
            return name
      };
      this.setName=function(value){
            this.name=value;
      };
}


最後補充說明一下書上關於閉包與變量的那個例子

function createFunctions(){
      var result=new Array();
      for(var i=0;i<10;i++){
            result[i]=function(num){
                  return function(){
                        return num;
                  };
            }(i);
      }
}
這裏我當時想的是爲什麼不直接改成返回一個return num;不就OK了,爲啥還要返回一個function(){ return num;}?其實,如果直接return num這裏保存在result裏的數字確實也是0,1,2。。。。9,但是問題是這就不是閉包作用的體現了,這裏主要是想講給結果result返回一個函數,因爲閉包的作用的體現很多時候就體現在返回一個函數。因此這裏返回一個函數作爲result的一項,加“()”運行這些函數項將會得到0,1,2。。。。9的結果。而如果想return num的話,那還莫不如直接result[i]=i;呢。。。。還何必費這事。。。= =愚蠢。。。

歡迎各位拍磚指正。








發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章