函數是進行模組化程式設計的基礎,編寫複雜的Ajax應用程序,必須對函數有更深入的了解。 JavaScript中的函數不同於其他的語言,每個函數都是作為一個物件被維護和運行的。透過函數物件的性質,可以很方便的將一個函數賦值給一個變數或將函數傳遞為參數。在繼續講述之前,先來看看函數的使用語法:
function func1(…){…}
var func2=function(…){…};
var func3=function func4(…){…};
var func5=new Function();
這些都是宣告函數的正確語法。它們和其他語言中常見的函數或先前介紹的函數定義方式有著很大的差異。那麼在JavaScript中為什麼可以這麼寫呢?它所遵循的語法是什麼呢?下面將介紹這些內容。
認識函數物件(Function Object)
可以用function關鍵字定義一個函數,並為每個函數指定一個函數名,透過函數名稱來進行呼叫。當JavaScript解釋執行時,函數都是被維護為一個對象,這就是要介紹的函數物件(Function Object)。
函數物件與其他使用者所定義的物件有著本質上的區別,這一類物件稱為內部對象,例如日期物件(Date)、陣列物件(Array)、字串物件(String)都屬於內部物件。這些內建物件的建構器是由JavaScript本身定義的:透過執行new Array()這樣的語句傳回一個對象,JavaScript內部有一套機制來初始化傳回的對象,而不是由使用者來指定物件的建構方式。
在JavaScript中,函數物件對應的類型是Function,正如陣列物件對應的類型是Array,日期物件對應的類型是Date一樣,可以透過new Function()來建立一個函數對象,也可以透過function關鍵字來創建一個物件。為了便於理解,我們比較函數物件的創建和數組物件的創建。先看陣列物件:下面兩行程式碼都是建立一個陣列物件myArray:
var myArray=[];
//等價於
var myArray=new Array();
同樣,下面的兩段程式碼也都是建立一個函數myFunction:
function myFunction(a,b){
return a+b;
}
//等價於
var myFunction=new Function("a","b","return a+bvar myFunction=new Function("a","b","return a+b"," );
透過和建構數組物件語句的比較,可以清楚的看到函數物件本質,前面介紹的函數宣告是上述程式碼的第一種方式,而在解釋器內部,當遇到這種語法時,就會自動建構一個Function對象,將函數當作一個內部的物件來儲存和運作。從這裡也可以看到,一個函數物件名稱(函數變數)和一個普通變數名稱具有相同的規範,都可以透過變數名稱來引用這個變量,但是函數變數名稱後面可以跟上括號和參數列表來進行函數調用。
用new Function()的形式來建立一個函數不常見,因為一個函數體通常會有多條語句,如果將它們以一個字串的形式作為參數傳遞,程式碼的可讀性差。以下介紹一下其使用語法:
var funcName=new Function(p1,p2,,pn,body);
參數的類型都是字串,p1到pn表示所創建函數的參數名稱列表,body表示所創建函數的函數體語句,funcName就是所建立函數的名稱。可以不指定任何參數來建立一個空函數,不指定funcName建立一個無名函數,當然那樣的函數沒有任何意義。
需要注意的是,p1到pn是參數名稱的列表,即p1不僅能代表一個參數,它也可以是一個逗號隔開的參數列表,例如下面的定義是等價的:
new Function( "a", "b", "c", "return a+b+c")
new Function("a, b, c", "return a+b+c")
new Function("a,b ", "c", "return a+b+c")
JavaScript引入Function類型並提供new Function()這樣的語法是因為函數物件添加屬性和方法就必須藉助於Function這個類型。
函數的本質是一個內部對象,由JavaScript解釋器決定其運作方式。透過上述程式碼所建立的函數,在程式中可以使用函數名進行呼叫。本節開頭列出的函數定義問題也得到了解釋。注意可直接在函數宣告後面加上括號就表示建立完成後立即進行函數調用,例如:
var i=function (a,b){
return a+b;
}(1,2);
alert return a+b;
}(1,2);
}(1,2);
alert return a+b;🎜}(1,2);🎜alert return a+b;🎜}(1,2);🎜alert return a+b;🎜}(1,2); (i);🎜 這段程式碼會顯示變數i的值等於3。 i是表示傳回的值,而不是建立的函數,因為括號「(」比等號「=」有更高的優先權。這樣的程式碼可能不常用,但當使用者想在很長的程式碼段中進行模組化設計或想避免命名衝突,這是一個不錯的解決方法。function funcName(){
//函數體
}
//等價於
var funcName=function(){
//了一個無名函數,只是讓一個變數指向了這個無名函數。在使用上僅有一點區別,就是:對於有名函數,它可以出現在呼叫之後再定義;而對於無名函數,它必須是在呼叫之前就已經定義。例如:
<script language="JavaScript" type="text/javascript">
func();
var func=function(){
</script>
這段語句將產生func未定義的錯誤,而:
<script language="JavaScript" type="text/javascript">
<!--
func(); ){
}
//-->
</script>
則能夠正確執行,下面的語句也能正確執行:
< >
<!--
func();
var someFunc=function func(){
}
//-->
</script</script型的語言,但它會在函數呼叫時,檢查整個程式碼中是否存在對應的函數定義,這個函數名稱只有是透過function funcName()形式定義的才會有效,而不能是匿名函數。
函數物件和其他內部物件的關係
除了函數對象,還有很多內部對象,例如:Object、Array、Date、RegExp、Math、Error。這些名稱實際上表示一個類型,可以透過new操作符傳回一個物件。然而函數物件和其他物件不同,當用typeof得到一個函數物件的類型時,它仍然會傳回字串“function”,而typeof一個陣列物件或其他的物件時,它會傳回字串“object”。下面的程式碼範例了typeof不同類型的情況:
alert(typeof(new Function()));
alert(typeof(Array));alert(typeof( Object));
alert(typeof(new Array()));alert(typeof(new Date()));
alert(typeof(new Object())); 運行這段程式碼可以發現:前面4條語句都會顯示“function”,而後面3條語句則顯示“object”,可見new一個function實際上是傳回一個函數。這與其他的對像有很大的不同。其他的型別Array、Object等都會透過new操作符傳回一個普通物件。儘管函數本身也是一個對象,但它與普通的對象還是有區別的,因為它同時也是對象構造器,也就是說,可以new一個函數來返回一個對象,這在前面已經介紹。所有typeof回傳「function」的物件都是函數物件。也稱這樣的對象為構造器(constructor),因而,所有的構造器都是對象,但不是所有的對像都是構造器。
既然函數本身也是一個對象,它們的類型是function,聯想到C++、Java等物件導向語言的類別定義,可以猜測到Function類型的作用所在,那就是可以為函數物件本身定義一些方法和屬性,借助於函數的prototype對象,可以很方便地修改和擴充Function類型的定義,例如下面擴展了函數類型Function,為其增加了method1方法,作用是彈出對話框顯示"function":
Function.prototype. method1=function(){
alert("function(){
alert("function");
}
return a+b+c;
}method1. (); 注意最後一個語句:func1.method1.mehotd1(),它呼叫了method1這個函數物件的method1方法。雖然看起來有點容易混淆,但仔細觀察一下文法還是很明確的:這是一個遞歸的定義。因為method1本身也是函數,所以它同樣具有函數物件的屬性和方法,所有對Function類型的方法擴充都具有這樣的遞歸性質。
Function是所有函數物件的基礎,而Object則是所有物件(包含函數物件)的基礎。在JavaScript中,任何一個物件都是Object的實例,因此,可以修改Object這個類型來讓所有的物件有一些通用的屬性和方法,修改Object型別是透過prototype來完成的:
Object.prototype.getType =function(){
return typeof(this);
}
var array1=new Array();
function func1(a,b){
alert(func1.getType());
上面的程式碼為所有的物件增加了getType方法,作用是傳回該物件的型別。兩個alert語句分別會顯示「object」和「function」。
將函數作為參數傳遞
在前面已經介紹了函數對象本質,每個函數都被表示為一個特殊的對象,可以方便的將其賦值給一個變量,再通過這個變量名進行函數調用。作為一個變量,它可以以參數的形式傳遞給另一個函數,這在前面介紹JavaScript事件處理機制中已經看到過這樣的用法,例如下面的程式將func1作為參數傳遞給func2:
function func1( theFunc){
theFunc();
}
function func2(){
alert("ok");
}
func1(func2);〔2);㟀句中給了一個字句加號theFunc,再由func1內部進行theFunc的呼叫。事實上,將函數當作參數傳遞,或是將函數賦值給其他變數是所有事件機制的基礎。