在工作中經常用到JavaScript,今天總結一下JavaScript編寫類的幾種寫法以及這幾種寫法的優缺點,關於JavaScript編寫類的方式,在網上看到很多,而且每個人的寫法都不太一樣,經常看到的就是以下幾種方式。
1、構造函數方式
用構造函數模擬"類",在其內部用this關鍵字指代實例對象。
基本語法:
function 類名(){
this.屬性名;//公共屬性
var 屬性名;//私有屬性
/*凡是定義類的公共屬性和公共方法都要使用this*/
//定義類的公共函數
this.函數名=function(){
.....
}
//定義類的私有函數
function 函數名(){
......
}
}
范例:
1 /*定義一個Person類*/ 2 function Person(_name,_age,_salary){ 3 //Person類的公開屬性,類的公開屬性的定義方式是:”this.屬性名“ 4 this.name=_name; 5 //Person類的私有屬性,類的私有屬性的定義方式是:”var 屬性名“ 6 var age=_age;//私有屬性 7 var salary=_salary;//私有屬性 8 9 /*定義私有屬性Age的對外公開訪問方法*/ 10 this.setAge = function(intAge) { 11 age = intAge; 12 } 13 /*定義私有屬性Age的對外公開訪問方法*/ 14 this.getAge = function() { 15 return age; 16 } 17 18 //定義Person類的公開方法(特權方法),類的公開方法的定義方式是:”this.functionName=function(){.....}“ 19 this.Show=function(){ 20 document.writeln("在公開方法里面訪問類的私有屬性是允許的,age="+age+"\t"+"salary="+salary);//在公開方法里面訪問類的私有屬性是允許的 21 } 22 //公共方法 23 this.publicMethod = function(){ 24 document.writeln("在公開方法里面訪問類的私有方法是允許的"); 25 privateFn();//在公開方法里面調用類的私有方法 26 privateFn2();//在公開方法里面調用類的私有方法 27 } 28 /* 29 定義Person類的私有方法(內部方法), 30 類的私有方法的定義方式是:”function functionName(){.....}“, 31 或者 var functionName=function(){....} 32 */ 33 function privateFn(){ 34 document.writeln("我是Person類的私有函數privateFn"); 35 } 36 37 var privateFn2=function(){ 38 document.writeln("我是Person類的私有函數privateFn2"); 39 } 40 }
測試Person類
1 var p1 = new Person("孤傲蒼狼",24,2300); 2 var p2 = new Person("白虎神皇",24,2300); 3 document.write("<pre>"); 4 document.writeln("p1 instanceof Person的結果是:"+(p1 instanceof Person));//p1是Person類的實例,結果是true 5 document.writeln("p2 instanceof Person的結果是:"+(p2 instanceof Person));//p2是Person類的實例,結果是true 6 //當==兩邊的內容是對象或者是對象的函數屬性時,則比較內存地址是否相等 7 document.writeln("當==兩邊的內容是對象或者是對象的函數屬性時,則比較內存地址是否相等"); 8 document.writeln("比較p1和p2這兩個對象的show方法的內存地址是否一樣:p1.show== p2.show的結果是:"+(p1.show == p2.show));//false 9 document.writeln("p1.show == p2.show的結果是:"+(p1.show == p2.show)+",這證明p1對象和p2對象不是共享一個show方法,在內存中show方法的代碼有2份,存放在兩塊內存區域"); 10 document.writeln("name是Person類定義的public屬性,可以使用類的對象去直接訪問類的public屬性"); 11 document.writeln("p1.name="+p1.name);//訪問公有屬性,這是可以正常訪問的 12 document.writeln("age和salary是Person類定義的private屬性,不能使用類的對象去直接訪問類私有屬性,這是訪問不了的,結果都是undefined"); 13 document.writeln("p1.age="+p1.age+","+"p1.salary="+p1.salary)//不能使用類的對象去直接訪問類私有屬性,這是訪問不了的,結果都是undefined 14 p1.show();//調用類的公共函數,這次允許的 15 p1.publicMethod();//調用類的公共函數,這次允許的 16 p1.setAge(24);//使用public方法setAge方法為私有屬性age賦值 17 document.writeln("使用public方法getAge方法獲取私有屬性age的值,p1.getAge()="+p1.getAge());//使用getAge方法獲取私有屬性age的值 18 //document.writeln("p1.privateFn():"+p1.privateFn()+" p1.privateFn2():"+p1.privateFn2());//不能使用類的對象去調用類的私有方法,這里會報錯”對象不支持此屬性或者方法 19 document.write("</pre>");
測試結果:
這種方式的優點是:可以根據參數來構造不同的對象實例 ,每個對象的屬性一般是不相同的,缺點是構造每個實例對象時,方法不能共享,Person類里面定義的那些方法,p1對象有一份,p2也有一份,那么在內存中就得開辟兩塊內存空間來分別存儲p1的方法和p2的方法,這樣就造成了內存的浪費。對於一個類的不同實例對象,這些對象的屬性一般是不相同的,但是方法是相同的,所以節約內存的做法就是把方法放到內存的一塊區域中存放,然后每個實例對象都從這塊內存中取出方法。
2、原型方式
需要說明的是,使用原型方式編寫JavaScript類是無法給類添加私有屬性和私有方法的,使用原型方式添加的屬性和方法都是public的。
寫法一:
1 /*定義一個Person類*/ 2 function Person(_name,_age,_weight,_height){ 3 this.init(_name,_age,_weight,_height); 4 } 5 6 /*使用原型的方式定義Person類的public屬性:name,age,weight,height,使用原型的方式添加的屬性都是public的*/ 7 Person.prototype.name; 8 Person.prototype.age; 9 Person.prototype.weight; 10 Person.prototype.height; 11 /*使用原型的方式給Person類添加public方法,使用原型的方式添加的方法都是public的*/ 12 /*使用原型的方式給Person類添加init方法*/ 13 Person.prototype.init = function(_name,_age,_weight,_height) { 14 if(_name != undefined && _age!=undefined && _weight!=undefined && _height!=undefined){ 15 this.name = _name; 16 this.age = _age; 17 this.weight=_weight; 18 this.height=_height; 19 document.writeln("this.name="+this.name+",this.age="+this.age+",this.weight="+this.weight+",this.height="+this.height); 20 } 21 22 } 23 /*使用原型的方式給Person類添加show方法*/ 24 Person.prototype.show = function(){ 25 document.writeln("show method"); 26 }
測試Person類
1 document.write("<pre>"); 2 var p1 = new Person("孤傲蒼狼",24,115,160); 3 var p2 = new Person("白虎神皇",25,120,170); 4 var p3 = new Person(); 5 p3.init("玄天邪帝",26,130,180);//調用public方法init初始化p3對象 6 document.writeln("p1 instanceof Person的結果是:"+(p1 instanceof Person));//p1是Person類的實例,結果是true 7 document.writeln("p2 instanceof Person的結果是:"+(p2 instanceof Person));//p2是Person類的實例,結果是true 8 document.writeln("p3 instanceof Person的結果是:"+(p3 instanceof Person));//p3是Person類的實例,結果是true 9 //當==兩邊的內容是對象或者是對象的函數屬性時,則比較內存地址是否相等 10 document.writeln("當==兩邊的內容是對象或者是對象的函數屬性時,則比較內存地址是否相等"); 11 document.writeln("比較p1和p2這兩個對象的show方法的內存地址是否一樣:p1.show == p2.show的結果是:"+(p1.show == p2.show));//true 12 document.writeln("p1.show == p2.show的結果是:"+(p1.show == p2.show)+",這證明p1對象和p2對象共享一個show方法,在內存中show方法的代碼只有一份,存放在內存的一塊區域");//true 13 document.writeln("p1.name="+p1.name+",p1.age="+p1.age+",p1.weight="+p1.weight+",p1.height="+p1.height);//訪問公有屬性,這是可以正常訪問的 14 document.writeln("p2.name="+p2.name+",p2.age="+p2.age+",p2.weight="+p2.weight+",p2.height="+p2.height);//訪問公有屬性,這是可以正常訪問的 15 p3.name="滅世魔尊";//為公共屬性重新賦值 16 document.writeln("p3.name="+p3.name);//訪問公有屬性,這是可以正常訪問的 17 p1.show();//調用類的公共函數,這次允許的 18 document.write("</pre>");
測試結果:
寫法二:
使用原型方式給類定義public屬性和public方法更加優雅的寫法,我個人推薦使用這種方式,這種方式看起來比較舒服
1 /*定義類Person2*/ 2 function Person2(){ 3 4 } 5 6 /*使用原型方式給類定義public屬性和public方法更加優雅的寫法*/ 7 Person2.prototype = { 8 name:"",//public屬性 9 age:0,//public屬性 10 weight:0,//public屬性 11 height:0,//public屬性 12 /*public方法*/ 13 init:function(_name,_age,_weight,_height) { 14 this.name = _name; 15 this.age = _age; 16 this.weight=_weight; 17 this.height=_height; 18 document.writeln("this.name="+this.name+",this.age="+this.age+",this.weight="+this.weight+",this.height="+this.height); 19 }, 20 /*public方法*/ 21 show:function(){ 22 document.writeln("show method"); 23 } 24 };
測試代碼:
1 document.write("<pre>"); 2 var p2_1 = new Person2(); 3 var p2_2 = new Person2(); 4 p2_1.init("孤傲蒼狼",24,115,160); 5 p2_2.init("白虎神皇",25,120,170); 6 document.writeln("p2_1.name="+p2_1.name+",p2_1.age="+p2_1.age+",p2_1.weight="+p2_1.weight+",p2_1.height="+p2_1.height);//訪問公有屬性,這是可以正常訪問的 7 document.writeln("p2_2.name="+p2_2.name+",p2_2.age="+p2_2.age+",p2_2.weight="+p2_2.weight+",p2_2.height="+p2_2.height);//訪問公有屬性,這是可以正常訪問的 8 document.writeln("p2_1 instanceof Person2的結果是:"+(p2_1 instanceof Person2));//p2_1是Person2類的實例,結果是true 9 document.writeln("p2_2 instanceof Person2的結果是:"+(p2_2 instanceof Person2));//p2_2是Person2類的實例,結果是true 10 //當==兩邊的內容是對象或者是對象的函數屬性時,則比較內存地址是否相等 11 document.writeln("當==兩邊的內容是對象或者是對象的函數屬性時,則比較內存地址是否相等"); 12 document.writeln("比較p2_1和p2_2這兩個對象的init方法的內存地址是否一樣:p2_1.init == p2_2.init的結果是:"+(p2_1.init == p2_2.init));//true 13 p2_1.name="滅世魔尊";//為公共屬性重新賦值 14 document.writeln("p2_1.name="+p2_1.name);//訪問公有屬性,這是可以正常訪問的 15 p2_1.show();//調用類的公共函數,這次允許的 16 document.write("</pre>");
測試結果:
原型方式的優點:所有對象實例都共享類中定義的方法,這樣就沒有造成內存浪費。缺點,第一,不能定義類的私有屬性和私有方法,第二,給在創建對象,給對象的屬性初始化時,需要額外寫一個初始化對象的方法。
3、構造函數+原型
構造函數方式和原型方式都有各自的優缺點,因此可以把這兩種方式合並起來,用構造函數方式來定義類的屬性(public屬性,private屬性),用原型方式來定義類的方法(public方法)。互補不足,這就有了第三種寫法。
1 /*定義一個Person類*/ 2 function Person(_name,_age,_salary){ 3 //在Person類內部定義類的public屬性和private屬性以及private方法 4 //Person類的公開屬性,類的公開屬性的定義方式是:”this.屬性名“ 5 this.name=_name; 6 //Person類的私有屬性,類的私有屬性的定義方式是:”var 屬性名“ 7 var age=_age;//私有屬性,只能在類內部使用 8 var salary=_salary;//私有屬性,只能在類內部使用 9 /* 10 定義Person類的私有方法(內部方法),只能在類內部使用 11 類的私有方法的定義方式是:”function functionName(){.....}“, 12 或者 var functionName=function(){....} 13 */ 14 function privateFn(){ 15 document.write("<pre>"); 16 document.writeln("我是Person類的私有屬性age,只能在Person類內部使用,初始化后age="+age); 17 document.writeln("我是Person類的私有函數privateFn,只能在Person類內部使用"); 18 document.write("</pre>"); 19 } 20 21 var privateFn2=function(){ 22 document.write("<pre>"); 23 document.writeln("我是Person類的私有屬性salary,只能在Person類內部使用,初始化后salary="+salary); 24 document.writeln("我是Person類的私有函數privateFn2,只能在Person類內部使用"); 25 document.write("</pre>"); 26 } 27 28 privateFn();//在Person類內部調用私有方法 29 privateFn2();//在Person類內部調用私有方法 30 } 31 32 //使用prototype原型方式定義的方法(public方法)是無法訪問類的私有屬性和私有方法的 33 //使用prototype原型方式定義Person類的方public方法 34 Person.prototype={ 35 setName:function(_name){ 36 this.name = _name; 37 //privateFn();//不能調用Person類定義的私有方法privateFn(),會報錯:缺少對象 38 }, 39 getName:function(){ 40 return this.name; 41 }, 42 show:function(){ 43 document.writeln("公開方法show"); 44 }, 45 //公共方法 46 publicMethod:function(){ 47 document.writeln("公開方法publicMethod"); 48 } 49 };
測試代碼:
1 var p1 = new Person("孤傲蒼狼",24,2300); 2 var p2 = new Person("白虎神皇",25,3000); 3 document.write("<pre>"); 4 document.writeln("p1 instanceof Person的結果是:"+(p1 instanceof Person));//p1是Person類的實例,結果是true 5 document.writeln("p2 instanceof Person的結果是:"+(p2 instanceof Person));//p2是Person類的實例,結果是true 6 //當==兩邊的內容是對象或者是對象的函數屬性時,則比較內存地址是否相等 7 document.writeln("當==兩邊的內容是對象或者是對象的函數屬性時,則比較內存地址是否相等"); 8 document.writeln("比較p1和p2這兩個對象的show方法的內存地址是否一樣:p1.show== p2.show的結果是:"+(p1.show == p2.show));//true 9 document.writeln("p1.show == p2.show的結果是:"+(p1.show == p2.show)+",這證明p1對象和p2對象共享一個show方法,在內存中show方法的代碼有1份,存放在1塊內存區域"); 10 document.writeln("name是Person類定義的public屬性,可以使用類的對象去直接訪問類的public屬性"); 11 document.writeln("p1.name="+p1.name);//訪問公有屬性,這是可以正常訪問的 12 document.writeln("age和salary是Person類定義的private屬性,不能使用類的對象去直接訪問類私有屬性,這是訪問不了的,結果都是undefined"); 13 document.writeln("p1.age="+p1.age+","+"p1.salary="+p1.salary)//不能使用類的對象去直接訪問類私有屬性,這是訪問不了的,結果都是undefined 14 p1.show();//調用類的公共函數,這次允許的 15 p1.publicMethod();//調用類的公共函數,這次允許的 16 p1.setName("玄天邪帝");//調用類的公共函數設置為name屬性重新賦值 17 document.writeln("p1.getName="+p1.getName()); 18 //document.writeln("p1.privateFn():"+p1.privateFn()+" p1.privateFn2():"+p1.privateFn2());//不能使用類的對象去調用類的私有方法,這里會報錯”對象不支持此屬性或者方法 19 document.write("</pre>");
運行結果:
第三種方式通過前兩種方式的結合,算是達到了一個比較理想的寫法了,可以通過傳參構造對象實例,對象實例都共享同一份方法不造成內存浪費。第三種方式在開發中用得最多,我本人也是采用這種方式來編寫JavaScript類。