1. 程式人生 > >對javascript匿名函式的理解(透徹版)

對javascript匿名函式的理解(透徹版)

源連結: http://www.cnblogs.com/chenxianbin89/archive/2010/01/28/1658392.html


網上很多解釋,我無法理解,我想知道原理。。。這篇文章應該可以透徹一點

 

Query片段:

view plain copy to clipboard print ?
  1. (function(){  
  2. //這裡忽略jQuery所有實現  
  3. })();  

  半年前初次接觸jQuery的時候,我也像其他人一樣很興奮地想看看原始碼是什麼樣的。然而,在看到原始碼的第一眼,我就迷糊了。為什麼只有一個匿 名函式又沒看到執行(當然是運行了……),就能有jQuery這麼個函式庫了?於是,我抱著疑問來到CSDN。結果相信現在很多人都很清楚了(因為在我之 後也不乏來者,呵呵~)。當一個匿名函式被括起來,然後再在後面加一個括號,這個匿名函式就能立即執行起來!真神奇哦!

  嘿嘿!胡鬧到此為止。在這一節,我們碰到的jQuery片段是一組立即執行的匿名函式。而這種用法在論壇上也曾引起過激辯——這段程式碼究竟屬不 屬於閉包呢?帶著這個疑問,我們從基礎開始,分析每個關鍵要素,尋找屬於自己的答案。(沒錯,自己的答案!在我看來,所有理論只是形式,只要它有利於我們 的應用實現,就是可取的——黑貓白貓,抓到老鼠的就是好貓!)

  要說匿名函式,我們首先要由函式本身說起。函式的定義如下:

函式是將唯一的輸出值賦予給每一輸入的“法則”。

  當然,這只是數學上的定義。但是,在計算機程式語言中,函式的定義也八九不離十。因為,我們都知道,計算機中的函式,也類似數學定義中的描述,它是將輸入的若干資料,經過程式碼設定的邏輯操作處理後,返回唯一的輸出的一組程式碼組合塊。

——當然,特例是,輸入的資料為空或輸出的資料為空,或者兩者都為空。

  下面,我們先初步瞭解一下和匿名函式相關的概念。

  • 函式宣告(function 語句)

  要使用一個函式,我們就得首先宣告它的存在。而我們最常用的方式就是使用function語句來定義一個函式,如:

view plain copy to clipboard print ?
  1. function abc(){  
  2.   // code to process  
  3. }  

   當然,你的函式也可以是帶引數的,甚至是帶返回值的。

view plain copy to clipboard print ?
  1. function abc(x,y){  
  2.   return x+y;  
  3. }  

  但是,無論你怎麼去定義你的函式,JS直譯器都會把它翻譯成一個Function物件。例如,你在定義上面的其中一個例子的函式號,再輸入如下程式碼:

view plain copy to clipboard print ?
  1. alert(typeof abc);// "function"  

  你的瀏覽器就會彈出提示框,提示你abc是一個Function物件。那麼Function物件究竟是什麼呢?

  • Function 物件

  Function物件是JavaScript裡面的固有物件,所有的函式實際上都是一個Function物件。關於這個方面的討論,我們留到下一個專題節。我們先看看,Function物件能不能直接運用建構函式建立一個新的函式呢?答案是肯定的。例如:

view plain copy to clipboard print ?
  1. var abc = new Function("x","y","return x*y;");  
  2. alert(abc(2,3)); // "6"  

  相信大家現在對如何宣告一個函式應該是有所瞭解了。那麼什麼才是匿名函式呢?

  • 宣告匿名函式

  顧名思義,匿名函式就是沒有實際名字的函式。例如,我們把上面的例子中,函式的名字去掉,再判斷一下他是不是一個函式:

view plain copy to clipboard print ?
  1. alert(typeof function(){});// "function"  
  2. alert(typeof function(x,y){return x+y;});// "function"  
  3. alert(typeof new Function("x","y","return x*y;"))// "function"  

  我們可以很容易地看到,它們全都是Function物件,換言之,他們都是函式,但是他們都有一個特點——沒有名字。所以我們把他們稱作“匿名函式”。然而,正因為他們沒有“名字”,我們也沒有辦法找到他們。這就引申瞭如何去呼叫一個匿名函式的問題了。

  • 匿名函式的呼叫

  要呼叫一個函式,我們必須要有方法定位它,引用它。所以,我們會需要幫它找一個名字。例如:

view plain copy to clipboard print ?
  1. var abc=function(x,y){  
  2.   return x+y;  
  3. }  
  4. alert(abc(2,3)); // "5"  

  上面的操作其實就等於換個方式去定義函式,這種用法是我們比較頻繁遇到的。例如我們在設定一個DOM元素事件處理函式的時候,我們通常都不會為他們定名字,而是賦予它的對應事件引用一個匿名函式。

  對匿名函式的呼叫其實還有一種做法,也就是我們看到的jQuery片段——使用()將匿名函式括起來,然後後面再加一對小括號(包含引數列表)。我們再看一下以下例子:

view plain copy to clipboard print ?
  1. alert((function(x,y){return x+y;})(2,3));// "5"  
  2. alert((new Function("x","y","return x*y;"))(2,3));// "6"  

  很多人或許會奇怪,為什麼這種方法能成功呼叫呢?覺得這個應用奇怪的人就看一下我以下這段解釋吧。

  大家知道小括號的作用嗎?小括號能把我們的表示式組合分塊,並且每一塊,也就是每一對小括號,都有一個返回值。這個返回值實際上也就是小括號中表達式的返回值。所以,當我們用一對小括號把匿名函式括起來的時候,實際上小括號對返回的,就是一個匿名函式的Function物件。因此,小括號對加上匿名函式就如同有名字的函式般被我們取得它的引用位置了。所以如果在這個引用變數後面再加上引數列表,就會實現普通函式的呼叫形式。

  不知道以上的文字表述大家能不能看明白,如果還是理解不了的話,再看一下以下的程式碼試試吧。

view plain copy to clipboard print ?
  1. var abc=function(x,y){return x+y;};// 把匿名函式物件賦給abc  
  2. // abc的constructor就和匿名函式的constructor一樣了。也就是說,兩個函式的實現是一樣的。  
  3. alert((abc).constructor==(function(x,y){return x+y;}).constructor);  

  PS:constructor是指建立物件的函式。也就是函式物件所代表的函式體。

  總之,將其(被小括號包含的匿名函式)理解為括號表示式返回的函式物件,然後就可以對這個函式物件作正常的引數列表呼叫了。(前面這裡犯了個錯 誤,只有函式表示式還是不能直接呼叫函式的,去掉匿名函式括號必須要伴隨將表示式賦值。也就是(function(){alert(1)})()應該是與 a=function(){alert(1)}()等價,不能連a=都去掉。)

  • 閉包

   閉包是什麼?閉包是指某種程式語言中的程式碼塊允許一級函式存在並且在一級函式中所定義的自由變數能不被釋放,直到一級函式被釋放前,一級函式外也能應用這些未釋放的自由變數。

  怎樣?看得一頭冒汗吧……沒事,我也是(雖然是我是瞭解的,只是表達能力的問題)。讓我們換個更加簡單的方法說明:閉包,其實是一種語言特性,它是指的是程式設計語言中,允許將函式看作物件,然後能像在物件中的操作搬在函式中定義例項(區域性)變數,而這些變數能在函式中儲存到函式的例項物件銷燬為止,其它程式碼塊能通過某種方式獲取這些例項(區域性)變數的值並進行應用擴充套件。

  不知道這麼再解釋後會否更加清晰,如果還是不明白,那麼我們再簡化一下:閉包,其實就是指程式語言中能讓程式碼呼叫已執行的函式中所定義的區域性變數。

  現在我們看一個例子:

view plain copy to clipboard print ?
  1. var abc=function(y){  
  2. var x=y;// 這個是區域性變數  
  3. return function(){  
  4.   alert(x++);// 就是這裡呼叫了閉包特性中的一級函式區域性變數的x,並對它進行操作  
  5.   alert(y--);// 引用的引數變數也是自由變數  
  6. }}(5);// 初始化  
  7. abc();// "5" "5"  
  8. abc();// "6" "4"  
  9. abc();// "7" "3"  
  10. alert(x);// 報錯!“x”未定義!  

  看到這裡,你能判斷究竟jQuery的那個程式碼片段是否閉包了嗎?

  以我的理解來說吧。是否應用了閉包特性,必須確定該段程式碼有沒有 最重要的要素:未銷燬的區域性變數。那麼很顯然,沒有任何實現的匿名函式不可能應用了閉包特性。但如果匿名函式裡面有實現呢?那也還得確定它的實現中有沒有 用到那些未銷燬的區域性變數。所以如果問你那個開篇中的jQuery程式碼片段是應用了JS裡的什麼特性?那麼它只是匿名函式與匿名函式的呼叫而已。但是,它 隱含了閉包的特性,並且隨時可以實現閉包應用。因為JS天生就是有這個特性的!(這只是我的理解,我也想知道你的理解,歡迎交流!關於閉包,有機會還是獨立再開一個專題吧!)