-------------------該文摘自於匿名教程總結,但願對初學js的同窗有幫助,由於它解決了我學習js的衆多迷惑。。。
函數是進行模塊化程序設計的基礎,編寫複雜的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+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(i);
這段代碼會顯示變量i的值等於3。i是表示返回的值,而不是建立的函數,由於括號「(」比等號「=」有更高的優先級。這樣的代碼可能並不經常使用,但當用戶想在很長的代碼段中進行模塊化設計或者想避免命名衝突,這是一個不錯的解決辦法。
須要注意的是,儘管下面兩種建立函數的方法是等價的:
如下是引用片斷:
function funcName(){
//函數體
}
//等價於
var funcName=function(){
//函數體
}
但前面一種方式建立的是有名函數,然後面是建立了一個無名函數,只是讓一個變量指向了這個無名函數。在使用上僅有一點區別,就是:對於有名函數,它能夠出如今調用以後再定義;而對於無名函數,它必須是在調用以前就已經定義。例如:
如下是引用片斷:
<script language="JavaScript" type="text/javascript">
<!--
func();
var func=function(){
alert(1)
}
//-->
</script>
這段語句將產生func未定義的錯誤,而:
如下是引用片斷:
<script language="JavaScript" type="text/javascript">
<!--
func();
function func(){
alert(1)
}
//-->
</script>
則可以正確執行,下面的語句也能正確執行:
如下是引用片斷:
<script language="JavaScript" type="text/javascript">
<!--
func();
var someFunc=function func(){
alert(1)
}
//-->
</script>
因而可知,儘管JavaScript是一門解釋型的語言,但它會在函數調用時,檢查整個代碼中是否存在相應的函數定義,這個函數名只有是經過function funcName()形式定義的纔會有效,而不能是匿名函數。
函數對象和其餘內部對象的關係
除了函數對象,還有不少內部對象,好比:Object、Array、Date、RegExp、Math、Error。這些名稱實際上表示一個類型,可 以經過new操做符返回一個對象。然而函數對象和其餘對象不一樣,當用typeof獲得一個函數對象的類型時,它仍然會返回字符串「function」,而 typeof一個數組對象或其餘的對象時,它會返回字符串「object」。下面的代碼示例了typeof不一樣類型的狀況:
如下是引用片斷:
alert(typeof(Function)));
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");
}
function func1(a,b,c){
return a+b+c;
}
func1.method1();
func1.method1.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){
return a+b;
}
alert(array1.getType());
alert(func1.getType());
上面的代碼爲全部的對象添加了getType方法,做用是返回該對象的類型。兩條alert語句分別會顯示「object」和「function」。
將函數做爲參數傳遞
在前面已經介紹了函數對象本質,每一個函數都被表示爲一個特殊的對象,能夠方便的將其賦值給一個變量,再經過這個變量名進行函數調用。做爲一個變量,它 能夠以參數的形式傳遞給另外一個函數,這在前面介紹JavaScript事件處理機制中已經看到過這樣的用法,例以下面的程序將func1做爲參數傳遞給 func2:
如下是引用片斷:
function func1(theFunc){
theFunc();
}
function func2(){
alert("ok");
}
func1(func2);
在最後一條語句中,func2做爲一個對象傳遞給了func1的形參theFunc,再由func1內部進行theFunc的調用。事實上,將函數做爲參數傳遞,或者是將函數賦值給其餘變量是全部事件機制的基礎。
例如,若是須要在頁面載入時進行一些初始化工做,能夠先定義一個init的初始化函數,再經過window.onload=init;語句將其綁定到頁面載入完成的事件。這裏的init就是一個函數對象,它能夠加入window的onload事件列表。
傳遞給函數的隱含參數:arguments
當進行函數調用時,除了指定的參數外,還建立一個隱含的對象——arguments。arguments是一個相似數組但不是數組的對象,說它相似是 由於它具備數組同樣的訪問性質,能夠用arguments[index]這樣的語法取值,擁有數組長度屬性length。arguments對象存儲的是 實際傳遞給函數的參數,而不侷限於函數聲明所定義的參數列表,例如:
如下是引用片斷:
function func(a,b){
alert(a);
alert(b);
for(var i=0;i<arguments.length;i++){
alert(arguments[i]);
}
}
func(1,2,3);
代碼運行時會依次顯示:1,2,1,2,3。所以,在定義函數的時候,即便不指定參數列表,仍然能夠經過arguments引用到所得到的參數,這給 編程帶來了很大的靈活性。arguments對象的另外一個屬性是callee,它表示對函數對象自己的引用,這有利於實現無名函數的遞歸或者保證函數的封 裝性,例如使用遞歸來計算1到n的天然數之和:
如下是引用片斷:
var sum=function(n){
if(1==n)return 1;
else return n+sum(n-1);
}
alert(sum(100));
其中函數內部包含了對sum自身的調用,然而對於JavaScript來講,函數名僅僅是一個變量名,在函數內部調用sum即至關於調用一個全局變量,不能很好的體現出是調用自身,因此使用arguments.callee屬性會是一個較好的辦法:
如下是引用片斷:
var sum=function(n){
if(1==n)return 1;
else return n+arguments.callee(n-1);
}
alert(sum(100));
callee屬性並非arguments不一樣於數組對象的唯一特徵,下面的代碼說明了arguments不是由Array類型建立:
如下是引用片斷:
Array.prototype.p1=1;
alert(new Array().p1);
function func(){
alert(arguments.p1);
}
func();
運行代碼能夠發現,第一個alert語句顯示爲1,即表示數組對象擁有屬性p1,而func調用則顯示爲「undefined」,即p1不是arguments的屬性,因而可知,arguments並非一個數組對象。
函數的apply、call方法和length屬性
JavaScript爲函數對象定義了兩個方法:apply和call,它們的做用都是將函數綁定到另一個對象上去運行,二者僅在定義參數的方式有所區別:
如下是引用片斷:
Function.prototype.apply(thisArg,argArray);
Function.prototype.call(thisArg[,arg1[,arg2…]]);
從函數原型能夠看到,第一個參數都被取名爲thisArg,即全部函數內部的this指針都會被賦值爲thisArg,這就實現了將函數做爲另一個 對象的方法運行的目的。兩個方法除了thisArg參數,都是爲Function對象傳遞的參數。下面的代碼說明了apply和call方法的工做方式:
如下是引用片斷:
//定義一個函數func1,具備屬性p和方法A
function func1(){
this.p="func1-";
this.A=function(arg){
alert(this.p+arg);
}
}
//定義一個函數func2,具備屬性p和方法B
function func2(){
this.p="func2-";
this.B=function(arg){
alert(this.p+arg);
}
}
var obj1=new func1();
var obj2=new func2();
obj1.A("byA"); //顯示func1-byA
obj2.B("byB"); //顯示func2-byB
obj1.A.apply(obj2,["byA"]); //顯示func2-byA,其中[「byA」]是僅有一個元素的數組,下同
obj2.B.apply(obj1,["byB"]); //顯示func1-byB
obj1.A.call(obj2,"byA"); //顯示func2-byA
obj2.B.call(obj1,"byB"); //顯示func1-byB
能夠看出,obj1的方法A被綁定到obj2運行後,整個函數A的運行環境就轉移到了obj2,即this指針指向了obj2。一樣obj2的函數B也能夠綁定到obj1對象去運行。代碼的最後4行顯示了apply和call函數參數形式的區別。
與arguments的length屬性不一樣,函數對象還有一個屬性length,它表示函數定義時所指定參數的個數,而非調用時實際傳遞的參數個數。例以下面的代碼將顯示2:
如下是引用片斷:
function sum(a,b){
return a+b;
}
alert(sum.length);
javascript