代碼如下:
(function(){
//這裡忽略jquery所有實作
})();
(function(){ //這裡忽略jquery所有實作 })();
半年前初次接觸jquery的時候,我也像其他人一樣很興奮地想看看源碼是什麼樣的。然而,在看到源碼的第一眼,我就迷糊了。為什麼隻有一個匿 名函數又沒看到運作(當然是運作了……),就能有jquery這麼個函數庫了?于是,我抱着疑問來到csdn。結果相信現在很多人都很清楚了(因為在我之 後也不乏來者,呵呵~)。當一個匿名函數被括起來,然後再在後面加一個括号,這個匿名函數就能立即運作起來!真神奇哦!
嘿嘿!胡鬧到此為止。在這一節,我們碰到的jquery片段是一組立即運作的匿名函數。而這種用法在論壇上也曾引起過激辯——這段代碼究竟屬不 屬于閉包呢?帶着這個疑問,我們從基礎開始,分析每個關鍵要素,尋找屬于自己的答案。(沒錯,自己的答案!在我看來,所有理論隻是形式,隻要它有利于我們 的應用實作,就是可取的——黑貓白貓,抓到老鼠的就是好貓!)
要說匿名函數,我們首先要由函數本身說起。函數的定義如下:
函數是将唯一的輸出值賦予給每一輸入的“法則”。
當然,這隻是數學上的定義。但是,在計算機程式設計語言中,函數的定義也八九不離十。因為,我們都知道,計算機中的函數,也類似數學定義中的描述,它是将輸入的若幹資料,經過代碼設定的邏輯操作處理後,傳回唯一的輸出的一組代碼組合塊。——當然,特例是,輸入的資料為空或輸出的資料為空,或者兩者都為空。
下面,我們先初步了解一下和匿名函數相關的概念。
函數聲明(function 語句)
要使用一個函數,我們就得首先聲明它的存在。而我們最常用的方式就是使用function語句來定義一個函數,如:
複制代碼 代碼如下:
function abc(){
// code to process
}
function abc(){ // code to process }
當然,你的函數也可以是帶參數的,甚至是帶傳回值的。
view plaincopy to clipboardprint?
function abc(x,y){
return x+y;
function abc(x,y){ return x+y; }
但是,無論你怎麼去定義你的函數,js解釋器都會把它翻譯成一個function對象。例如,你在定義上面的其中一個例子的函數号,再輸入如下代碼:
alert(typeof abc);// “function”
你的浏覽器就會彈出提示框,提示你abc是一個function對象。那麼function對象究竟是什麼呢?
function 對象
function對象是javascript裡面的固有對象,所有的函數實際上都是一個function對象。關于這個方面的讨論,我們留到下一個專題節。我們先看看,function對象能不能直接運用構造函數建立一個新的函數呢?答案是肯定的。例如:
var abc = new function(“x”,”y”,”return x*y;”);
alert(abc(2,3)); // “6”
var abc = new function(“x”,”y”,”return x*y;”); alert(abc(2,3)); // “6”
相信大家現在對如何聲明一個函數應該是有所了解了。那麼什麼才是匿名函數呢?
聲明匿名函數
顧名思義,匿名函數就是沒有實際名字的函數。例如,我們把上面的例子中,函數的名字去掉,再判斷一下他是不是一個函數:
alert(typeof function(){});// “function”
alert(typeof function(x,y){return x+y;});// “function”
alert(typeof new function(“x”,”y”,”return x*y;”))// “function”
alert(typeof function(){});// “function” alert(typeof function(x,y){return x+y;});// “function” alert(typeof new function(“x”,”y”,”return x*y;”))// “function”
我們可以很容易地看到,它們全都是function對象,換言之,他們都是函數,但是他們都有一個特點——沒有名字。是以我們把他們稱作“匿名函數”。然而,正因為他們沒有“名字”,我們也沒有辦法找到他們。這就引申了如何去調用一個匿名函數的問題了。
匿名函數的調用
要調用一個函數,我們必須要有方法定位它,引用它。是以,我們會需要幫它找一個名字。例如:
var abc=function(x,y){
alert(abc(2,3)); // “5”
var abc=function(x,y){ return x+y; } alert(abc(2,3)); // “5”
上面的操作其實就等于換個方式去定義函數,這種用法是我們比較頻繁遇到的。例如我們在設定一個dom元素事件處理函數的時候,我們通常都不會為他們定名字,而是賦予它的對應事件引用一個匿名函數。
對匿名函數的調用其實還有一種做法,也就是我們看到的jquery片段——使用()将匿名函數括起來,然後後面再加一對小括号(包含參數清單)。我們再看一下以下例子:
alert((function(x,y){return x+y;})(2,3));// “5”
alert((new function(“x”,”y”,”return x*y;”))(2,3));// “6”
alert((function(x,y){return x+y;})(2,3));// “5” alert((new function(“x”,”y”,”return x*y;”))(2,3));// “6”
很多人或許會奇怪,為什麼這種方法能成功調用呢?覺得這個應用奇怪的人就看一下我以下這段解釋吧。
大家知道小括号的作用嗎?小括号能把我們的表達式組合分塊,并且每一塊,也就是每一對小括号,都有一個傳回值。這個傳回值實際上也就是小括号中表達式的傳回值。是以,當我們用一對小括号把匿名函數括起來的時候,實際上小括号對傳回的,就是一個匿名函數的function對象。是以,小括号對加上匿名函數就如同有名字的函數般被我們取得它的引用位置了。是以如果在這個引用變量後面再加上參數清單,就會實作普通函數的調用形式。
不知道以上的文字表述大家能不能看明白,如果還是了解不了的話,再看一下以下的代碼試試吧。
var abc=function(x,y){return x+y;};// 把匿名函數對象賦給abc
// abc的constructor就和匿名函數的constructor一樣了。也就是說,兩個函數的實作是一樣的。
alert((abc).constructor==(function(x,y){return x+y;}).constructor);
var abc=function(x,y){return x+y;};// 把匿名函數對象賦給abc // abc的constructor就和匿名函數的constructor一樣了。也就是說,兩個函數的實作是一樣的。 alert((abc).constructor==(function(x,y){return x+y;}).constructor);
ps:constructor是指建立對象的函數。也就是函數對象所代表的函數體。
總之,将其(被小括号包含的匿名函數)了解為括号表達式傳回的函數對象,然後就可以對這個函數對象作正常的參數清單調用了。(前面這裡犯了個錯 誤,隻有函數表達式還是不能直接調用函數的,去掉匿名函數括号必須要伴随将表達式指派。也就是(function(){alert(1)})()應該是與 a=function(){alert(1)}()等價,不能連a=都去掉。)
閉包
閉包是什麼?閉包是指某種程式語言中的代碼塊允許一級函數存在并且在一級函數中所定義的自由變量能不被釋放,直到一級函數被釋放前,一級函數外也能應用這些未釋放的自由變量。
怎樣?看得一頭冒汗吧……沒事,我也是(雖然是我是了解的,隻是表達能力的問題)。讓我們換個更加簡單的方法說明:閉包,其實是一種語言特性,它是指的是程式設計語言中,允許将函數看作對象,然後能像在對象中的操作搬在函數中定義執行個體(局部)變量,而這些變量能在函數中儲存到函數的執行個體對象銷毀為止,其它代碼塊能通過某種方式擷取這些執行個體(局部)變量的值并進行應用擴充。
不知道這麼再解釋後會否更加清晰,如果還是不明白,那麼我們再簡化一下:閉包,其實就是指程式語言中能讓代碼調用已運作的函數中所定義的局部變量。
現在我們看一個例子:
var abc=function(y){
var x=y;// 這個是局部變量
return function(){
alert(x++);// 就是這裡調用了閉包特性中的一級函數局部變量的x,并對它進行操作
alert(y–);// 引用的參數變量也是自由變量
}}(5);// 初始化
abc();// “5” “5”
abc();// “6” “4”
abc();// “7” “3”
alert(x);// 報錯!“x”未定義!
var abc=function(y){ var x=y;// 這個是局部變量 return function(){ alert(x++);// 就是這裡調用了閉包特性中的一級函數局部變量的x,并對它進行操作 alert(y–);// 引用的參數變量也是自由變量 }}(5);// 初始化 abc();// “5” “5” abc();// “6” “4” abc();// “7” “3” alert(x);// 報錯!“x”未定義!
看到這裡,你能判斷究竟jquery的那個代碼片段是否閉包了嗎?
以我的了解來說吧。是否應用了閉包特性,必須确定該段代碼有沒有 最重要的要素:未銷毀的局部變量。那麼很顯然,沒有任何實作的匿名函數不可能應用了閉包特性。但如果匿名函數裡面有實作呢?那也還得确定它的實作中有沒有 用到那些未銷毀的局部變量。是以如果問你那個開篇中的jquery代碼片段是應用了js裡的什麼特性?那麼它隻是匿名函數與匿名函數的調用而已。但是,它 隐含了閉包的特性,并且随時可以實作閉包應用。