1.遞歸函數是在一個函數通過調用自身的情況下構成的。
~~~
function factorial(num){
if (num<=1){
return 1;
} else {
return num*arguments.callee(num-1);
}
}
~~~
~~~
var factorial = (function f(num){
if (num<=1){
return 1;
} else {
return num*f(num-1);
}
});
~~~
以上代碼創建了一個名為f()的**命名函數表達式**,然后將它賦給了另一個變量,函數的名字f仍然有效,所以遞歸調用照樣能正確完成。這種方式在嚴格模式和非嚴格模式下都行得通。
2.閉包是指有權訪問另一個函數作用域中的變量的**函數**。
3.創建閉包的常見方式,就是在一個函數內部創建另一個函數。
~~~
function createComparisionFunction(propertyName){
return function (object1, object2) {
var value1 = object1[propertyName];
var value2 = onbect2[propertyName];
if (value1 < value2){
return -1;
} else if (value1 > value2){
return 1;
} else {
return 0;
}
};
}
~~~
**當createComparisionFunction()函數返回后,其執行環境的作用域會被銷毀,但他的活動對象仍然會留在內存中;直到匿名函數被銷毀后,createComparisionFunction()的活動對象才會被銷毀。**
~~~
//創建函數
var compareNames = createComparisionFunction('name');
//調用函數
var result = compareNames({name:'Nicolas'},{name:'Greg});
//解除對匿名函數的引用(以便釋放內存)
compareNames = null;
~~~
4.匿名函數的執行環境具有全局性,因此其this對象通常指向window。但有時候由于編寫閉包的方式不同,這一點可能不回那么明顯。
~~~
var name = 'The window';
var object = {
name: 'My Object',
getNameFunc: function(){
return function(){
return this.name;
};
}
};
alert(object.getNameFunc()()); //'The window'(在非嚴格模式下)
~~~
5.對已經沒有用處的、閉包外部函數(保存著對象)的變量,應該將其設置為null以避免內存泄露。
~~~
function assignHandler(){
var element = document.getElementById('someElement');
var id = element.id;
element.onclick = function(){
alert(id);
};
element = null;
}
~~~
6.用作塊級作用域(通常稱為私有作用域)的匿名函數語法如下:
~~~
(function(){
//這里是塊級作用域
})();
~~~
無論什么時候,只要臨時需要一些變量,就可以使用私有作用域。這種技術經常在全局作用域中被用在函數外部,從而限制向全局作用域中添加過多的變量和函數。這種做法可以減少閉包占用的內存問題,因為沒有指向匿名函數的引用。只要函數執行完畢,就可以立即銷毀其作用域鏈了。
7.私有變量包括函數的參數、局部變量和在函數內部定義的其他函數。
8.有權訪問私有變量和私有函數的公有方法稱為**特權方法**。有兩種在對象上創建特權方法的方式。第一種是在構造函數中定義特權方法。
~~~
function MyObject(){
//私有變量和私有函數
var privateVariable = 10;
function privateFunction(){
return false;
}
//特權方法
this.publicMethod = function(){
privateVariable++;
return privateFunction();
};
}
~~~
利用私有和特權成員,可以隱藏那些不應該被直接修改的數據:
~~~
function Person(name){
this.getName = function(){
return name;
};
this.setName = function(value){
name = value;
};
}
~~~
以上代碼的構造函數定義了兩個特權方法:getName()和setName()。這兩個方法都可以在構造函數外部使用,而且都有權訪問私有變量name。但在Person構造函數外部,沒有任何辦法訪問name。不過,在函數中定義特權方法也有一個缺點,那就是必須使用構造函數模式來達到這個目的。(方法無法復用)
9.通過在私有作用域中定義私有變量或函數,同樣也可以創造特權方法,即**靜態私有變量**。
~~~
(function(){
//私有變量和私有函數
var privateVariable = 10;
function privateFunction(){
return false;
}
//構造函數
MyObject = function(){};
//公有、特權方法
MyObject.prototype.publicMethod = function(){
privateVariable++;
return privateFunction();
};
})();
~~~
這個模式與在構造函數中定義特權方法的主要區別,就在于私有變量和函數是由實例共享的。由于特權方法是在原型上定義的,因此所有實例都是用同一個函數。而這個特權方法,作為一個閉包,總是保存著對包含作用域的引用。以這種方式創建靜態私有變量會因為使用原型而增加代碼復用,但每個實例都沒有自己的私有變量了。
10.前面的模式是用于為自定義類型創建私有變量和特權方法的。而**模塊模式**則是為單例創建私有變量和特權方法。所謂單例,指的就是只有一個實例的對象。模塊模式通過為單例添加私有變量和特權方法能夠使其得到增強,其語法形式如下:
~~~
var singleton = function(){
//私有變量和私有函數
var privateVariable = 10;
function privateFunction(){
return false;
}
//特權/公有方法和屬性
return {
publiceProperty: true,
publicMethod: function(){
privateVariable++;
return privateFunction();
}
};
}();
~~~
由于這個返回的對象是在匿名函數內部定義的,因此它的公有方法有權訪問私有變量和函數。從本質上講,這個對象字面量定義的是單例的公共接口。這種模式在需要對單例進行某些初始化,同時又需要維護其私有變量時是非常有用的。例如:
~~~
var application = function(){
//私有變量和函數
var conponents = new Array();
//初始化
components.push(new BaseComponent());
//公共
return {
getComponentCount: function(){
return components.length;
},
registgerComponent: function(component){
if (typeof component == 'object'){
components.push(component);
}
};
}();
~~~
在web應用程序中,經常需要使用一個單例來管理應用程序級的信息。這個簡單的例子創建了一個用于管理組件的application對象。在創建這個對象的過程中,首先聲明了一個私有的components數組,并向這個數組添加了一個BaseComponent的新實例。而返回對象的getComponentCount()和registerComponent()方法,都是有權訪問數組components的特權方法。前者只是返回已注冊的組件數目,后者用于注冊新組件。
簡而言之,如果必須創建一個對象并以某些數據對其進行初始化,同時還要公開一些能夠訪問這些私有數據的方法,那么就可以使用模塊模式。
11.**增強的模塊模式**適合那些單例必須是某種類型的實例,同時還必須添加某些屬性和(或)方法對其加以增強的情況。改寫前面的例子:
~~~
var application = function(){
//私有變量和函數
var conponents = new Array();
//初始化
components.push(new BaseComponent());
//創建application的一個局部副本
var app = new BaseComponent();
//公共接口
app.getComponentCount = function(){
return components.length;
};
app.registgerComponent = function(component){
if (typeof component == 'object'){
components.push(component);
}
};
return app;
}();
~~~