定義函數(shù)的方式有兩種:一種是函數(shù)聲明,另一種就是函數(shù)表達(dá)式。
//函數(shù)聲明的語法是這樣的
function functionName(arg0, arg1, arg2) {//函數(shù)體} ```
//函數(shù)表達(dá)式有很多種,最常見的的語法是這樣的
var functionName = function(arg0, arg1, arg2){//函數(shù)體}; ```
一、遞歸
function factorial(num){
if (num <= 1){
return 1;
} else {
return num * factorial(num-1);
}
}```
這是一個(gè)經(jīng)典的遞歸階乘函數(shù)。雖然這個(gè)函數(shù)表面看來沒什么問題,但下面的代碼卻可能導(dǎo)致它出錯(cuò)。
var anotherFactorial = factorial;
factorial = null;
alert(anotherFactorial(4)); //出錯(cuò)! ```
arguments.callee 是一個(gè)指向正在執(zhí)行的函數(shù)的指針,因此可以用它來實(shí)現(xiàn)對(duì)函數(shù)的遞歸調(diào)用,例如:
function factorial(num){
if (num <= 1){
return 1;
} else {
return num * arguments.callee(num-1);
}
} ```
在嚴(yán)格模式下,不能通過腳本訪問 arguments.callee,訪問這個(gè)屬性會(huì)導(dǎo)致錯(cuò)誤。不過,可以使用命名函數(shù)表達(dá)式來達(dá)成相同的結(jié)果。例如:
var factorial = (function f(num){
if (num <= 1){
return 1;
} else {
return num * f(num-1);
}
});```
以上代碼創(chuàng)建了一個(gè)名為 f()的命名函數(shù)表達(dá)式,然后將它賦值給變量 factorial。即便把函數(shù)賦值給了另一個(gè)變量,函數(shù)的名字 f 仍然有效,所以遞歸調(diào)用照樣能正確完成。這種方式在嚴(yán)格模式和非嚴(yán)格模式下都行得通。
二、閉包
閉包是指有權(quán)訪問另一個(gè)函數(shù)作用域中的變量的函數(shù)。創(chuàng)建閉包的常見方式,就是在一個(gè)函數(shù)內(nèi)部創(chuàng)建另一個(gè)函數(shù)
1. 閉包與變量
function createFunctions(){
var result = new Array();
for (var i=0; i < 10; i++){
result[i] = function(){
return i;
};
}
return result;
} //返回?cái)?shù)組內(nèi)部是十個(gè)10```
function createFunctions(){
var result = new Array();
for (var i=0; i < 10; i++){
result[i] = function(num){
return function(){
return num;
};
}(i);
}
return result;
} //返回?cái)?shù)組內(nèi)容是0-9```
2. 關(guān)于this對(duì)象
var name = "The Window";
var object = {
name : "My Object",
getNameFunc : function(){
return function(){
return this.name;
};
}
};
alert(object.getNameFunc()()); //"The Window"(在非嚴(yán)格模式下) ```
每個(gè)函數(shù)在被調(diào)用時(shí)都會(huì)自動(dòng)取得兩個(gè)特殊變量: this 和 arguments。內(nèi)部函數(shù)在搜索這兩個(gè)變量時(shí),只會(huì)搜索到其活動(dòng)對(duì)象為止,永遠(yuǎn)不可能直接訪問外部函數(shù)中的這兩個(gè)變量。不過,把外部作用域中的 this 對(duì)象保存在一個(gè)閉包能夠訪問到的變量里,就可以讓閉包訪問該對(duì)象了,如下所示。
var name = "The Window";
var object = {
name : "My Object",
getNameFunc : function(){
var that = this;
return function(){
return that.name;
};
}
};
alert(object.getNameFunc()()); //"My Object" ```
3. 模仿塊級(jí)作用域
JavaScript 沒有塊級(jí)作用域的概念。這意味著在塊語句中定義的變量,實(shí)際上是在包含函數(shù)中而非語句中創(chuàng)建的
function outputNumbers(count){
for (var i=0; i < count; i++){
alert(i);
}
alert(i); //計(jì)數(shù)
}```
這個(gè)函數(shù)中定義了一個(gè) for 循環(huán),而變量 i 的初始值被設(shè)置為 0。在 Java、 C++等語言中,變量 i只會(huì)在 for 循環(huán)的語句塊中有定義,循環(huán)一旦結(jié)束,變量 i 就會(huì)被銷毀。可是在 JavaScrip 中,變量 i是定義在 ouputNumbers()的活動(dòng)對(duì)象中的,因此從它有定義開始,就可以在函數(shù)內(nèi)部隨處訪問它。
在一個(gè)由很多開發(fā)人員共同參與的大型應(yīng)用程序中,過多的全局變量和函數(shù)很容易導(dǎo)致命名沖突。而通過創(chuàng)建私有作用域,每個(gè)開發(fā)人員既可以使用自己的變量,又不必?fù)?dān)心搞亂全局作用域。例如:
(function(){
var now = new Date();
if (now.getMonth() == 0 && now.getDate() == 1){
alert("Happy new year!");
}
})(); ```
4. 私有變量
增強(qiáng)的模塊模式
var singleton = function(){
//私有變量和私有函數(shù)
var privateVariable = 10;
function privateFunction(){
return false;
}
//創(chuàng)建對(duì)象
var object = new CustomType();
//添加特權(quán)/公有屬性和方法
object.publicProperty = true;
object.publicMethod = function(){
privateVariable++;return privateFunction();
};
//返回這個(gè)對(duì)象
return object;
}(); ```