function 函數(shù)名(參數(shù)列表){函數(shù)體;}
如果是創(chuàng)建匿名函數(shù),那就應(yīng)該是:
function(){函數(shù)體;}
因?yàn)槭悄涿瘮?shù),所以一般也不會(huì)有參數(shù)傳給他。
為什么要?jiǎng)?chuàng)建匿名函數(shù)呢?在什么情況下會(huì)使用到匿名函數(shù)。匿名函數(shù)主要有兩種常用的場(chǎng)景,一是回調(diào)函數(shù),二是直接執(zhí)行函數(shù)。
回調(diào)函數(shù),像ajax的異步操作,就需要回調(diào)函數(shù)。這里就不詳解。關(guān)于直接執(zhí)行函數(shù),我看一個(gè)例子就明白了:
代碼如下:
<script language="javascript">
var a = "a";
(function(){
var a="b";
alert(a);
})();
alert(a);
</script>
在上面這段代碼中,會(huì)順序輸出兩個(gè)alert框。第一個(gè)alert框內(nèi)容為b,第二個(gè)為a。大家看到什么好處了嗎?對(duì)的,使用函數(shù)直接執(zhí)行可以限定變量的作用域,使不同腳本的相同變量可以得以共存。
下面,我們先初步了解一下和匿名函數(shù)相關(guān)的概念。
函數(shù)聲明(function 語(yǔ)句),要使用一個(gè)函數(shù),我們就得首先聲明它的存在。而我們最常用的方式就是使用function 語(yǔ)句來(lái)定義一個(gè)函數(shù),如:
代碼如下:
function abc(){
// code to process
}
function abc(){ // code to process }
當(dāng)然,你的函數(shù)也可以是帶參數(shù)的,甚至是帶返回值的。
代碼如下:
view plaincopy to clipboardprint?
function abc(x,y){
return x+y;
}
function abc(x,y){ return x+y; }
但是,無(wú)論你怎么去定義你的函數(shù),JS 解釋器都會(huì)把它翻譯成一個(gè)Function 對(duì)象。例如,你在定義上面的其中一個(gè)例子的函數(shù)號(hào),再輸入如下代碼:
alert(typeof abc);// "function"
你的瀏覽器就會(huì)彈出提示框,提示你abc 是一個(gè)Function 對(duì)象。那么Function 對(duì)象究竟是什么呢?
Function 對(duì)象
Function 對(duì)象是JavaScript 里面的固有對(duì)象,所有的函數(shù)實(shí)際上都是一個(gè)Function 對(duì)象。關(guān)于這個(gè)方面的討論,我們留到下一個(gè)專題節(jié)。我們先看看,F(xiàn)unction 對(duì)象能不能直接運(yùn)用構(gòu)造函數(shù)創(chuàng)建一個(gè)新的函數(shù)呢?答案是肯定的。例如:
代碼如下:
var abc = new Function("x","y","return x*y;");
alert(abc(2,3)); // "6"
相信大家現(xiàn)在對(duì)如何聲明一個(gè)函數(shù)應(yīng)該是有所了解了。那么什么才是匿名函數(shù)呢?
聲明匿名函數(shù)
顧名思義,匿名函數(shù)就是沒(méi)有實(shí)際名字的函數(shù)。例如,我們把上面的例子中,函數(shù)的名字去掉,再判斷一下他是不是一個(gè)函數(shù):
代碼如下:
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"
我們可以很容易地看到,它們?nèi)际荈unction 對(duì)象,換言之,他們都是函數(shù),但是他們都有一個(gè)特點(diǎn)—— 沒(méi)有名字。所以我們把他們稱作“ 匿名函數(shù)” 。然而,正因?yàn)樗麄儧](méi)有“ 名字” ,我們也沒(méi)有辦法找到他們。這就引申了如何去調(diào)用一個(gè)匿名函數(shù)的問(wèn)題了。
匿名函數(shù)的調(diào)用
要調(diào)用一個(gè)函數(shù),我們必須要有方法定位它,引用它。所以,我們會(huì)需要幫它找一個(gè)名字。例如:
代碼如下:
var abc=function(x,y){
return x+y;
}
alert(abc(2,3)); // "5"
上面的操作其實(shí)就等于換個(gè)方式去定義函數(shù),這種用法是我們比較頻繁遇到的。例如我們?cè)谠O(shè)定一個(gè)DOM 元素事件處理函數(shù)的時(shí)候,我們通常都不會(huì)為他們定名字,而是賦予它的對(duì)應(yīng)事件引用一個(gè)匿名函數(shù)。
對(duì)匿名函數(shù)的調(diào)用其實(shí)還有一種做法,也就是我們看到的jQuery 片段—— 使用() 將匿名函數(shù)括起來(lái),然后后面再加一對(duì)小括號(hào)(包含參數(shù)列表)。我們?cè)倏匆幌乱韵吕樱?/P>
代碼如下:
alert((function(x,y){return x+y;})(2,3));// "5"
alert((new Function("x","y","return x*y;"))(2,3));// "6"
很多人或許會(huì)奇怪,為什么這種方法能成功調(diào)用呢?覺(jué)得這個(gè)應(yīng)用奇怪的人就看一下我以下這段解釋吧。
大家知道小括號(hào)的作用嗎?小括號(hào)能把我們的表達(dá)式組合分塊,并且每一塊,也就是每一對(duì)小括號(hào),都有一個(gè)返回值。這個(gè)返回值實(shí)際上也就是小括號(hào)中表達(dá)式的返回值。所以,當(dāng)我們用一對(duì)小括號(hào)把匿名函數(shù)括起來(lái)的時(shí)候,實(shí)際上小括號(hào)對(duì)返回的,就是一個(gè)匿名函數(shù)的Function 對(duì)象。因此,小括號(hào)對(duì)加上匿名函數(shù)就如同有名字的函數(shù)般被我們?nèi)〉盟囊梦恢昧?。所以如果在這個(gè)引用變量后面再加上參數(shù)列表,就會(huì)實(shí)現(xiàn)普通函數(shù)的調(diào)用形式。
不知道以上的文字表述大家能不能看明白,如果還是理解不了的話,再看一下以下的代碼試試吧。
代碼如下:
var abc=function(x,y){return x+y;};// 把匿名函數(shù)對(duì)象賦給abc
// abc 的constructor 就和匿名函數(shù)的constructor 一樣了。也就是說(shuō),兩個(gè)函數(shù)的實(shí)現(xiàn)是一樣的。
alert((abc).constructor==(function(x,y){return x+y;}).constructor);
PS :constructor 是指創(chuàng)建對(duì)象的函數(shù)。也就是函數(shù)對(duì)象所代表的函數(shù)體。
總之,將其(被小括號(hào)包含的匿名函數(shù))理解為括號(hào)表達(dá)式返回的函數(shù)對(duì)象,然后就可以對(duì)這個(gè)函數(shù)對(duì)象作正常的參數(shù)列表調(diào)用了。(前面這里犯了個(gè)錯(cuò)誤,只有函數(shù)表達(dá)式還是不能直接調(diào)用函數(shù)的,去掉匿名函數(shù)括號(hào)必須要伴隨將表達(dá)式賦值。也就是(function(){alert(1)})() 應(yīng)該是與 a=function(){alert(1)}() 等價(jià),不能連a= 都去掉。)
閉包
閉包是什么?閉包是指某種程序語(yǔ)言中的代碼塊允許一級(jí)函數(shù)存在并且在一級(jí)函數(shù)中所定義的自由變量能不被釋放,直到一級(jí)函數(shù)被釋放前,一級(jí)函數(shù)外也能應(yīng)用這些未釋放的自由變量。
怎樣?看得一頭冒汗吧…… 沒(méi)事,我也是(雖然是我是了解的,只是表達(dá)能力的問(wèn)題)。讓我們換個(gè)更加簡(jiǎn)單的方法說(shuō)明:閉包,其實(shí)是一種語(yǔ)言特性,它是指的是程序設(shè)計(jì)語(yǔ)言中,允許將函數(shù)看作對(duì)象,然后能像在對(duì)象中的操作般在函數(shù)中定義實(shí)例(局部)變量,而這些變量能在函數(shù)中保存到函數(shù)的實(shí)例對(duì)象銷毀為止,其它代碼塊能通過(guò)某種方式獲取這些實(shí)例(局部)變量的值并進(jìn)行應(yīng)用擴(kuò)展。
不知道這么再解釋后會(huì)否更加清晰,如果還是不明白,那么我們?cè)俸?jiǎn)化一下:閉包,其實(shí)就是指程序語(yǔ)言中能讓代碼調(diào)用已運(yùn)行的函數(shù)中所定義的局部變量。
現(xiàn)在我們看一個(gè)例子:
代碼如下:
var abc=function(y){
var x=y;// 這個(gè)是局部變量
return function(){
alert(x++);// 就是這里調(diào)用了閉包特性中的一級(jí)函數(shù)局部變量的x ,并對(duì)它進(jìn)行操作
alert(y--);// 引用的參數(shù)變量也是自由變量
}}(5);// 初始化
abc();// "5" "5"
abc();// "6" "4"
abc();// "7" "3"
alert(x);// 報(bào)錯(cuò)!“x” 未定義!
看到這里,你能判斷究竟jQuery 的那個(gè)代碼片段是否閉包了嗎?
以我的理解來(lái)說(shuō)吧。是否應(yīng)用了閉包特性,必須確定該段代碼有沒(méi)有最重要的要素:未銷毀的局部變量。那么很顯然,沒(méi)有任何實(shí)現(xiàn)的匿名函數(shù)不可能應(yīng)用了閉包特性。但如果匿名函數(shù)里面有實(shí)現(xiàn)呢?那也還得確定它的實(shí)現(xiàn)中有沒(méi)有 用到那些未銷毀的局部變量。所以如果問(wèn)你那個(gè)開(kāi)篇中的jQuery 代碼片段是應(yīng)用了JS 里的什么特性?那么它只是匿名函數(shù)與匿名函數(shù)的調(diào)用而已。但是,它 隱含了閉包的特性,并且隨時(shí)可以實(shí)現(xiàn)閉包應(yīng)用。
最常見(jiàn)的用法:
代碼如下:
(function() {
alert('water');
})();
當(dāng)然也可以帶參數(shù):
代碼如下
(function(o) {
alert(o);
})('water');
想用匿名函數(shù)的鏈?zhǔn)秸{(diào)用?很簡(jiǎn)單:
代碼如下:
(function(o) {
alert(o);
return arguments.callee;
})('water')('down');
常見(jiàn)的匿名函數(shù)都知道了,看看不常見(jiàn)的:
代碼如下:
~(function(){
alert('water');
})();//寫法有點(diǎn)酷~
void function(){
alert('water');
}();//據(jù)說(shuō)效率最高~
+function(){
alert('water');
}();
-function(){
alert('water');
}();
~function(){
alert('water');
}();
!function(){
alert('water');
}();
(function(){
alert('water');
}());//有點(diǎn)強(qiáng)制執(zhí)行的味道~
更多信息請(qǐng)查看IT技術(shù)專欄