定義函數的方式:
1.函數聲明
function functionName(arg0,arg1,arg2){ //函數體 }
2.函數表達式
var funtionName = function(arg0,arg1,arg2){ //函數體 }
匿名函數的用途:
1.遞歸
funtion factorial(num){
if(num <=1){ return 1}
else return{ num * factorial(num-1) }
}
var anotherFactorial = factorial;
factorial = null;
alert(anotherFactorial(4));//出錯,factorial不再是函數
return num * arguments.callee(num-1);
var factorial = (function f(num){
if(num <=1) return 1;
else return num*f(num-1);
})
2.閉包
function createComparisonFuncion(propertyName){
return function(object1,object2){
var value1 = object1[propertyName];
var value2 = object2[propertyName];
if(value1 < value2) return -1;
else if(value1 > value2) return 1;
else return 0;
}
}
2.1閉包與變量
function createFunctions(){
var result = new Array();
for (var i=0; i < 10; i++){
result[i] = function(){
return i;
};
}
return result;//每個函數都返回10
}
function createFunctions(){
var result = new Array();
for (var i=0; i < 10; i++){
result[i] = function(num){//result數組中的每個函數都有自己num變量的一個副本
return function(){
return num;
};
}(i);
}
return result;
}
2.2關於this對象
var name = "The Window";
var object = {
name : "My Object",
getNameFunc : function(){
var that = this;
return function(){
return that.name;
};
}
};
alert(object.getNameFunc()()); //"My Object"
2.3內存泄漏
function assignHandler(){
var element = document.getElementById("someElement");
element.onclick = function(){//無法減少element的引用數,只要匿名函數存在,element的引用數至少爲1,它所佔用的內存永遠不會被回收
alert(element.id);
};
}
function assignHandler(){
var element = document.getElementById("someElement");
var id = element.id;
element.onclick = function(){
alert(id);
};
element = null;
}
3.模仿塊級作用域
function outputNumbers(count){
for (var i=0; i < count; i++){
alert(i);
}
alert(i); //計數
}
(function(){
//塊級作用域
})()
function outputNumbers(count){
(function () {
for (var i=0; i < count; i++){
alert(i);
}
})();
alert(i); //導致一個錯誤!
}
4.私有變量
function MyObject(){
var privateVariable = 10;
function privateFunction(){
return false;
}
//特權方法
this.publicMethod = function(){
privateVariable++;
return privateFunction();
}//在創建MyObject的實例後,除了使用publicMethod這一個途徑外,沒有任何辦法可以直接訪問privateVariable和privateFunction
}
4.1靜態私有變量
(function(){
//私有變量和私有函數
var privateVariable = 10;
function privateFunction(){
return false;
}
//構造函數,使用了函數表達式(函數聲明只能創建局部函數)
MyObject = function(){
};//初始化未經聲明的變量,總會創建一個全局變量,所以MyObject是全局變量,能夠在私有作用域之外被訪問到
//公有/特權方法
MyObject.prototype.publicMethod = function(){
privateVariable++;
return privateFunction();
};
})();
(function(){
var name = "";
Person = function(value){
name = value;
};
Person.prototype.getName = function(){
return name;
};
Person.prototype.setName = function (value){
name = value;
};
})();
var person1 = new Person("Nicholas");
alert(person1.getName()); //"Nicholas"
person1.setName("Greg");
alert(person1.getName()); //"Greg"
var person2 = new Person("Michael");
alert(person1.getName()); //"Michael"
alert(person2.getName()); //"Michael"
- //在一個實例上調用 setName()會影響所有實例。而調用 setName()或新建一個 Person 實例都會賦予 name 屬性一個新值。結果就是所有實例都會返回相同的值
4.2模塊模式
var singleton = {
name : value,
method : function () {
//這裏是方法的代碼
}
};
var singleton = function(){
//私有變量和私有函數
var privateVariable = 10;
function privateFunction(){
return false;
}
//特權/公有方法和屬性
return {//匿名函數
publicProperty: true,
publicMethod : function(){
privateVariable++;
return privateFunction();
}
};
}();
var application = function(){//管理組件的對象
//私有變量和函數
var components = new Array();
//初始化
components.push(new BaseComponent());
//有權訪問數組components的特權方法
return {
getComponentCount : function(){
return components.length;
},
registerComponent : function(component){
if (typeof component == "object"){
components.push(component);
}
}
};
}();
4.3增強的模塊模式
var singleton = function(){
//私有變量和私有函數
var privateVariable = 10;
function privateFunction(){
return false;
}
//創建對象
var object = new CustomType();
//添加特權/公有屬性和方法
object.publicProperty = true;
object.publicMethod = function(){
privateVariable++;
return privateFunction();
};
//返回這個對象
return object;
}();
var application = function(){
//私有變量和函數
var components = new Array();
//初始化
components.push(new BaseComponent());
//創建 application 的一個局部副本
var app = new BaseComponent();
//公共接口
app.getComponentCount = function(){
return components.length;
};
app.registerComponent = function(component){
if (typeof component == "object"){
components.push(component);
}
};
//返回這個副本
return app;
}();
5.小結
- 函數表達式不同於函數聲明。函數聲明要求有名字,但函數表達式不需要。沒有名字的函數表達式也叫做匿名函數。
- 在無法確定如何引用函數的情況下,遞歸函數就會變得比較複雜;
- 遞歸函數應該始終使用 arguments.callee 來遞歸地調用自身,不要使用函數名——函數名可能會發生變化。
- 在後臺執行環境中,閉包的作用域鏈包含着它自己的作用域、包含函數的作用域和全局作用域。
- 通常,函數的作用域及其所有變量都會在函數執行結束後被銷燬。
- 但是,當函數返回了一個閉包時,這個函數的作用域將會一直在內存中保存到閉包不存在爲止。
- 創建並立即調用一個函數,這樣既可以執行其中的代碼,又不會在內存中留下對該函數的引用。
- 結果就是函數內部的所有變量都會被立即銷燬——除非將某些變量賦值給了包含作用域(即外部作用域)中的變量。
- 即使 JavaScript 中沒有正式的私有對象屬性的概念,但可以使用閉包來實現公有方法,而通過公有方法可以訪問在包含作用域中定義的變量。
- 有權訪問私有變量的公有方法叫做特權方法。
- 可以使用構造函數模式、原型模式來實現自定義類型的特權方法,也可以使用模塊模式、增強的模塊模式來實現單例的特權方法。