天天看點

深入了解JavaScript函數

深入了解JavaScript函數

一、引言

    從功能上了解,函數是一組可以随時運作的語句,是一段代碼塊,也是所謂的子程式。在JavaScript中,函數實質上也是一種對象,是Function對象。函數通常會有參數與傳回值(不是必須),在JavaScript中,函數的應用十分靈活,也有多種定義函數的方法。

二、幾種定義函數的方式

    在JavaScript中,可以通過函數語句來聲明和定義函數、可以通過函數表達式來将建立函數,也可以使用Function構造方法來建立函數對象。

1、使用函數語句來定義函數

    JavaScript中有一種特殊的文法來直接定義函數,示例如下:

//使用函數語句定義函數
function outputName(){
console.log("Jaki");
}      

函數語句有這樣的結構:function name(param...){}。其中function為函數語句的關鍵字,name為自定義的函數名稱,小括号中定義函數的形參,大括号中可以進行代碼的編寫。

如果僅僅對上面的代碼進行運作,你會發現程式并沒有執行任何行為,函數必須在調用時才會被執行,調用函數示例如下:

//使用函數語句定義函數
function outputName(){
console.log("Jaki");
}
outputName();//将列印Jaki      

上面的定義的函數是最簡單的函數形式,函數也可以通過傳入參數的差異來做不同的功能,例如修改上面的函數,使其傳入姓名并且進行列印輸出:

//使用函數語句定義函數
function outputName(name){
console.log(name);
}
outputName("張三");//将列印張三      

函數也可以提供一個傳回值,例如一個簡單的加法運算函數,如下:

function add(a,b){
return a+b;
}
var res = add(3,4);
console.log(res);//7      

在JavaScript中,函數的傳回值類型不需要特殊指定,直接使用return語句傳回即可。需要注意,實際上任何函數都是有傳回值的,如果函數體中沒有使用return語句顯式的進行傳回,則預設會傳回undefined。

    JavaScript中函數的定義與使用不一定非要按照順序進行,實際上也可以将函數的定義寫在使用之後(解釋器會在與處理階段解釋定義的函數),如下:

var res = add(3,4);
function add(a,b){
return a+b;
}
console.log(res);//7      

2.使用函數表達式來定義函數

    使用函數表達式來定義函數與函數語句的文法十分相似,示例代碼如下:

//使用函數表達式來定義函數
var addFunc = function(a,b){
    return a+b; 
};
var res = addFunc(3,3);
console.log(res);//6      

這種方式定義的函數實際上是建立了一個函數對象,之後将這個對象的引用指派給了一個變量,通過變量開發者可以通路和調用函數。需要注意,函數表達式與函數語句的最大差別在于其可以省略函數名,即可以定義匿名函數,但是這種方式定義的函數在函數定義之前是不能夠被調用的,這也很好了解,JavaScript解釋器在預處理期間隻是解析除了addFunc變量,但是并沒有對其表達式進行運算,這時函數并沒有被定義,例如下面的代碼将報錯:

var res = addFunc(3,3);
//使用函數表達式來定義函數
var addFunc = function(a,b){
return a+b; 
};      

其實函數語句與函數表達式定義的函數還有一點重大差別,函數語句定義後函數名實際上是無法修改的,而使用函數表達式定義的函數是将引用指派給了變量,變量可以重新指派,也可以将兩個變量指派為同一個函數對象的引用,示例如下:

//使用函數表達式來定義函數
var addFunc = function(a,b){
return a+b; 
};
//将addFunc的值指派給另一個變量
var addFunc2 = addFunc;
//将addFunc變量修改為整型值
addFunc = 3;
var res = addFunc2(3,3);
console.log(res);//6      

如果需要在函數内部調用函數本身,即需要定義遞歸函數時,函數表達式也可以進行函數命名,示例如下:

//定義一個遞歸階乘函數
var mathFunc = function mathF(a){
var res = a;
a--;
if (a>0) {
res *= mathFunc(a);
  }
return res;
};
var mathRes = mathFunc(5);
console.log(mathRes);//120      

需要注意,函數表達式定義的函數名隻能作用于函數内部。

3.使用Function構造函數

    前面有提到,函數在JavaScript中實際上也是一種對象,是以可以使用new關鍵字來進行函數對象的構造,示例如下:

//Function構造器
var myFunc = new Function("name","console.log(name)");
myFunc("Jaki");//Jaki      

Function構造其的格式如下:Function(param,param...,funcbody)。其中參數個數不固定,最後一個參數為建立的函數的函數體字元串,前面的參數全部都将作為函數的形參傳入函數體内。

    實際上,無論通過哪種方式建立的函數,其實質上都是Function對象,Function對象中也有一些内置的屬性,其中最長用的為arguments屬性。arguments屬性是實際傳入函數的參數數組,其length屬性為函數所傳入的參數個數,也就是說,開發者在定義函數的時候實際上可以不定義形參,在調用函數的時候直接進行實參的傳入,函數内部還是可以擷取到參數,JavaScript函數的對象特性使得其十分靈活,形參并不能決定函數的參數個數與類型,實參、函數體和傳回值這些都可以在運作時決定!示例如下:

function testFunc(){
console.log(arguments.length);
console.log(arguments);
}
/*
3
{ '0': '1', '1': 2, '2': 3 }
*/
testFunc('1',2,3);      

函數是功能完整的對象,實際上函數對象本身也有一個length屬性,這個屬性将擷取到函數預期形參的個數,即可以友善的擷取到函數預期需要傳入的參數的個數,例如:

function testFunc(){
console.log(arguments.length);
console.log(arguments);
}
function textFunc2(name){

}
console.log(testFunc.length); //0
console.log(textFunc2.length); //1      

三、了解函數語句與函數表達式

    函數語句是定義函數的一種文法,而函數表達式實際上就是傳回一個函數對象,是以函數表達式可以直接進行調用,請看如下代碼:

var addFuncS = function(a,b){
console.log(a+b); 
}(1,2);//3      

上面的代碼中的addFuncS已經不再是一個函數對象的引用,而是數值3。在函數表達式後面可以直接跟小括号傳參進行函數的調用,但是如下的寫法将會報錯:

function addFuncE(){
console.log("hello");
}();      

在JavaScript中函數表達式十分容易被誤認為是函數定義的文法,區分了上面兩種情況,你會發現這是在程式設計過程中十分危險,JavaScript中的函數文法有如下兩條規則:

1.當函數文法成為表達式的一部分時,其會被轉換成為函數表達式。

2.内嵌于非函數的其他代碼塊中時,函數文法會被轉換成函數表達式。

例子如下:

//函數文法被轉換成函數表達式
//1.函數文法成為表達式的一部分
(function f1(){console.log("f1")})();
//2.函數文法内嵌于非函數的代碼塊彙總
if (true) {
  (function f2(){
console.log("f2");
  })();
}      

繼續閱讀