一.定義函數html
定義函數的兩種方式:函數聲明和函數表達式閉包
1.函數聲明app
function functionName(arg0,arg1,…){函數
//函數體this
}prototype
①name屬性:訪問函數的名字3d
alert(funcitonName.name);//」functionName」指針
②函數聲明提高component
在代碼執行以前會先讀取函數聲明(解析器會率先讀取函數聲明)。因此函數聲明能夠放到調用它的語句後邊htm
2.函數表達式:建立函數再賦值給變量
var functionName=function(arg0,arg1,…){
//函數體
};
①函數聲明要求有名字,但函數表達式不須要。沒有名字的函數表達式叫匿名函數
②沒有函數聲明提高,因此使用前先賦值
sayName(); //出錯
var sayName=function(){
alert(「liuzhongyi」);
};
③在控制語句中應該使用函數表達式,而不是函數聲明
④使用匿名函數能夠將函數當作值來使用,將函數做爲其餘函數的返回值等
二.遞歸
遞歸函數:一個函數經過名字調用自身
但使用函數名調用自身時,只要函數名變化,遞歸函數將出現問題
①使用arguments.callee來調用自身:是一個指針,指向正在執行的函數
function myFunction(num){ if(num<=1){ return 1; }else{ return num * arguments.callee(num-1); } }
②使用函數表達式
var myFunction=(function f(num){ if(num<1){ return 1; }else{ return num*f(num-1); } });
即便將myFunction更名,f(num)依然能在函數內使用
三.閉包
閉包:有權訪問另外一個函數做用域中的變量 的函數
建立閉包:在函數內部建立另外一個函數
1.閉包原理
①執行環境和做用域鏈
做用域鏈本質是一個指向變量對象的指針列表,只引用但不實際包含變量對象
當函數執行完畢後,執行環境,做用域鏈及局部變量對象就會銷燬,內存中只保存全局做用域(全局變量對象)
②在一個函數內部定義的匿名函數將外部函數的活動對象(局部變量對象)添加到它的做用域鏈中
這個匿名函數被返回後,它的做用域鏈被初始化爲包含它本身的做用域,包含函數的做用域和全局做用域
函數在執行完畢後,其活動對象也不會銷燬,由於匿名函數的做用域鏈依然在引用這個活動對象
解除匿名函數的引用,活動對象被銷燬 myFunction=null;
2.閉包與變量
閉包只能取得包含函數中任何變量的最後一個值 //沒看懂
3.關於this對象
this對象是與函數運行時的執行環境綁定的,全局函數中this等於window,當函數做爲某個對象的方法調用時,this等於那個對象
var name="window"; var object={ name:"object", getThis:function(){ return function(){ return this.name; }; } }; alert(object.getThis()); //"window" 由於this可以訪問到全局變量中的name變量
this不能指向本身嗎??
var name="window"; var object={ name:"object", getThis:function(){ return this.name; } }; (object.getThis=object.getThis)(); //"window"
4.內存泄漏
閉包的做用域鏈中保存着一個HTML元素,那麼該元素將沒法被銷燬
由於BOM,COM對象是以COM對象的形式實現的,採用的是引用計數的垃圾收集策略,因爲閉包的存在,HTML元素的引用次數至少是1,因此不能被收回
四.模仿塊級做用域
function outputNumbers(count){ for(var i=0;i<count;i++){ alert(i); } var i; alert(i); //依然計次 }
Java,C中i只會在for循環的語句塊中有定義,循環一結束,變量i就會被銷燬。但JS中沒有塊級做用域,i定義在outputNumbers()的活動對象裏,並且會對後續的再次聲明視而不見
1.私有做用域:用匿名函數模仿塊級做用域
function關鍵字表示函數聲明的開始,將函數聲明轉換成函數表達式,只要給它加上一對圓括號。函數表達式能夠直接做爲值來取代函數名。調用函數就是在後邊加一對圓括號
(function{ //塊級做用域 })();
匿名函數中定義的任何變量,都會在執行結束時被銷燬
function outputNumbers(count){ (function(){ for(var i=0;i<count;i++){ alert(i); } })(); alert(i); //出錯 }
好處:
①私有做用域,每一個開發人員均可以定義本身的變量,而不用擔憂擾亂全局做用域
②能夠減小閉包占用的內存,由於沒有指向匿名函數的引用,函數執行完做用域鏈就能夠銷燬
2.私有變量
全部對象屬性都是公有的。(能夠在外部訪問對象屬性)
私有變量:函數的參數,局部變量,函數內部定義的其餘函數。在函數外部不能訪問這些變量
公有方法:能夠經過閉包來實現公有方法,而經過公有方法能夠訪問在包含做用域中定義的變量
特權方法:有權訪問私有變量的方法叫特權方法
自定義類型的特權方法
①在構造函數中定義特權方法
function Person(name){ this.getName=function(){ return this.name; }; this.setName=function(value){ this.name=value; }; } var person1=new Person("liu"); alert(person1.getName());// "liu" person1.setName("zhong"); alert(person1.getName()); //"zhong"
實現了在構造函數外部訪問了私有變量name
缺點:構造函數的弊端,每一個實例都會建立同一組相同的方法
②靜態私有變量
(function(){ var name=""; Person=function(value){ //聲明全局變量,使用函數表達式,由於函數聲明只能建立局部函數 name=value; }; Person.prototype.getName=function(){ return name; }; Person.prototype.setName=function(){ name=value; }; })(); var person1=new Person("liu"); alert(person1.getName()); //"liu" person1.setName("zhong"); alert(person1.getName());
在一個函數上調用私有變量會影響全部實例,每一個實例沒有本身的私有變量
單例的特權方法
①模塊模式
爲單例(只有一個實例的對象)建立私有變量和特權方法
當必須建立一個對象並以某些數據對其初始化,同時還要公開一些可以訪問這些私有數據的方法,就可使用模塊模式
var application=function(){ //私有變量和函數 var components=new Array(); //初始化 components.push("liu"); //公有方法 return{ getLength:function(){ return components.length; } }; }();
②加強的模塊模式
單例必須是某種類型的實例,同時還必須添加某些屬性和方法對其以加強的狀況
//application對象必須是BaseComponent的實例 var application=function(){ //私有變量和函數 var components=new Array(); //初始化 components.push(new BaseComponent()); //建立application的一個局部副本 var app=new BaseComponent(); //公共接口 app.getLength=funtion(){ return components.length; }; //返回這個副本 return app; }();