讓js具有面向對象的特性

需求分析:使用javascript時,難免會用到複雜的數據結構,當通過定義對象和方法無法解決問題時,就要考慮使用面向對象的方法

困難:js不支持類的定義,也就是說我們無法通過定義一個類來封裝我們想要的屬性和方法,可以直接定義一個對象而無需構造函數.

變通:對象中可以任意存放屬性和方法,那麼就可以使用對象來模擬出類

在使用js的時候,想必大家都是用過類似於以下形式的方式來爲設置對象:

var myObj={};
myObj.key = "value";
myObj.func = function(key,value){
	
		myObj.key = value;
	
}

以上代碼我們就定義了一個對象myObj,並且爲其定義了一個屬性key值爲value,定義了一個方法function,然後我們可以做一下測試:

myObj.func("key","value2");
alert(myObj.key);

結果是什麼呢?value2

可以看到我們成功定義了一個對象的屬性和方法,並通過方法修改了對象的屬性.但這裏有一個很大的缺點,這個對象只能"自我陶醉",無法像如果需要另外定義一個對象,賦予相同的屬性和方法,就需要重複以上的步驟.而面向對象中,類和對象的區別就在於,類是對象的模板,可以使用構造方法來定義一系列具有相同屬性和方法的對象.那麼模仿面向對象,我們作如下改進:

function myObj(key,value){
	this.key = key;
	this.value = value;
	this.func = function(key){
		if(this.key ==key ){
			return this.value;
		}else{
			return null;
		}
	}
}
該方式是基於定義方法的模式來模擬一個類,那麼該如何來使用呢?

var obj = new  myObj("key","value");
alert(obj.func("key"));
看一看是不是特別像java裏面的,定義一個類,然後使用構造方法定義對象,之後調用get方法返回value;

在這裏有必要說一下:myObj相當於類名,myObj(key,value)相當於構造方法,只是融爲一體了.使用this修飾的屬性或方法都是public類型的,定義完以後可以通過對象訪問.

如果需要定義私有的屬性,可以使用var來修飾.

這個方法是我目前最常用的方式.
然而如果想要使用更多的面向對象的特性,比如繼承,多態,可以用更好的辦法來定義類.

使用"構造函數法"最大的問題是創建對象時需要用new來創建,這樣的方式雖然好用,卻是一種浪費內存的行爲,也就是說,每創建一個對象,所有的屬性和方法都會重新分配內存,其實使用js模擬類時畢竟不是真正的類,有些屬性和方法都可以是公用的,這讓我們想到了java的單例模式和靜態變量.

javascript規定,所有的構造函數都有一個屬性,prototype指向另一個對象,該對象下的所有屬性和方法都會被構造函數繼承,也就是說所有的static 的屬性和方法我們可以定義在prototype上面,例如:

function myObj(key,value){
	this.key = key;
	this.value = value;
	this.func = function(key){
		if(this.key ==key ){
			return this.value;
		}else{
			return null;
		}
	}
}
myObj.prototype.type = "staticType";
myObj.prototype.get = function(){}

這樣無論創建多少個對象,他們擁有的屬性type和方法get都是共享的了。

使用this和prototype時會把代碼的結構搞的有些混亂,那些對js不是非常熟練的同學們就無法讀懂和使用這些代碼,那麼怎麼樣進行改進讓我們更易使用呢?

這裏推薦一種極簡主義方法,也就是拋棄this和prototype關鍵字,模仿單例模式創建對象。

例如:要創建一個Animal類:

var Animal={
    getInstance:function(){
	var animal = {};
	animal.name="凱特";
	animal.run = function(){
		alert("100km/h");
	}; 
	return animal;
    }
};


原理很簡單,對象裏面定義了一個方法屬性getInstance,該方法就是我們在java中常見的單例模式,也可以理解爲工廠模式。如果想要創建對象,就去調用構造方法:

var a = Animal.getInstance();
a.run();
Animal跟類十分相似吧,getInstance就是個靜態方法。

面向對象封裝性已經說過了,以下說一說繼承

再定義一個類(對象):

var Cat = {
	
    getInstance:function(){
	var cat= Animal.getInstance();
	cat.name="kite";
	cat.catchMouse = function(){
	    alert("can do");
	}; 
	return cat;
    }
};
類Cat擁有一個相同的構造方法getInstance,只是Cat創建對象的時候是用Animal爲模板創建的,再加上自己的屬性和方法,就實現了繼承,測試一下:

var cat1 = Cat.getInstance();
cat1.run();
cat1.catchMouse();
可以證明,我們確實可以調用run()和catchMouse()也就是說繼承的問題得到解決了,那麼多態呢?子類是否能夠覆蓋父類的方法呢,子類的屬性能否重寫父類的屬性呢?

繼續測試:

alert(cat1.name);
可以得出結論,cat1.name的值已經被替換了,至於方法當然也是這般。下面我們需要考慮一個問題,如何獲得共享的屬性,就像Java裏面的static屬性一樣(java中的static變量屬於類變量,不提倡通過對象調用,但可以調用,所有的對象調用都是同一狀態),我們該怎麼做呢?

var Cat = {
	legNum:4,
    getInstance:function(){
	var cat= Animal.getInstance();
	cat.name="kite";
	cat.catchMouse = function(){
	    alert("can do");
	}; 
	cat.getLegNum = function(){
		cat.legNum = Cat.legNum;
	}
	cat.changeLegNum = function(legNum){
		Cat.legNum = legNum;
	}
	return cat;
    }
};



測試一下legNum屬性是不是共享的:

var cat1 = Cat.getInstance();
var cat2 = Cat.getInstance();
alert(cat1.getLegNum()==cat2.getLegNum());
cat1.changeLegNum(3);
alert(cat1.getLegNum()==cat2.getLegNum());
可以看到兩次打印出來的結果都是true,數據已經實現了共享,要注意,共享變量時依附於類的,在實現數據共享的過程中,要獲得類的變量,修改變量也是最終實現類變量的修改。





發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章