javascript基礎編程の物件導向編程
javascript是解釋型的語言,在編譯時間和運行時之間沒有明顯區別,因此需要更動態方法。javascript沒有正式的類的概念,我們可以使用在運行時建立新的物件類型來替代,並且可以隨時更改已有對象的屬性。
javascript是一種基於原型的物件導向的語言,即每個對象有個原型,對象從原型中繼承屬性和方法。當訪問對象的屬性或調用對象的方法時,解譯器首先檢查對象是否有個同名的執行個體屬性或方法。如果有,就使用執行個體屬性或方法;如果沒有,解譯器就檢查對象的原型中是否有適當的屬性或方法。在這樣一個特殊的環境下,該類型(類)的所有對象公用方法和屬性可以被封裝在原型中,每個對象可以有代表該對象特定資料的執行個體屬性。
在javascript中,原型關係是遞迴的。即,對象的原型也是一個對象,而原型的本身可能還有一個原型。這就意味著如果被訪問的屬性探索不是對象的執行個體屬性,也不是對象的原型的屬性,解譯器將沿著原型鏈搜尋到原型的原型。如果還沒有找到,就繼續沿原型鏈向上搜尋。javascript中object是所有對象的超類,所以object是它搜尋的終點。如果object中還沒有找到,那麼返回的值就是undefined。如果是在調用方法,則拋出error。
一、javascript物件導向基礎
1、對象的建立
javascript中的對象使用一個new運算子和一個建構函式建立。建構函式是一個特殊類型的函數,它通過初始化對象要佔據的記憶體,來準備一個新對象的使用。我們可以使用一個不帶參數的建構函式,也可以使用一個帶參數的建構函式,來建立對象。如:
var city = new String();var city = new String('上海'); 2、對象銷毀和記憶體回收
對象和其他變數都需要使用記憶體,而一個電腦中記憶體資源是有限的。為了防止潛在的記憶體不足,某些程式設計語言強制要求程式員仔細的管理他們程式中記憶體的使用。幸運的是,javascript並不要求程式員管理記憶體。當javascript建立對象時,解譯器自動給對象分配記憶體。當對象使用完畢後,解譯器自動清理對象所用記憶體,即記憶體回收。
但是,如果我們的對象代碼中包含了大量的資料操作,最好將不需要的資料用null來替換,以儘快釋放資源。
var book = new Book();....大量資料操作後.......book = null;
如果對相同資料有多個引用,必須確保所有的引用設為null。否則,解譯器將保留這些資料,以備再次需要。
3、動態添加對象屬性
var obj = new Object('hello world');alert(obj.name);//undefinedobj.name='jiajia';alert(obj.name);//jiajia 動態添加的屬性,我們稱之為執行個體屬性,之所以叫執行個體屬性,是因為它們僅出現在它們添加到的特定對象或執行個體中。與此相反還有一個公用屬性,比如說string的length屬性,所有string對象都具有這一公用屬性。執行個體屬性有利於為了某些特殊用途對已存在的對象進行補充或擴充。
4、動態刪除對象屬性
var mystring = new String('hello world');mystring.isTrue = true;delete mystring.isTrue;alert(mystring.isTrue);//undefined; java程式員注意,javascript中delete與Java中不同,它僅僅用在對象中刪除屬性或者從數組中刪除元素,但是我們不能刪除對象本身。
5、動態添加對象的方法
像添加屬性方式一樣,方法也可以動態添加:
var mystring = new String('hello world');function alertSay(){alert('不行');};mystring.sayno=alertSay;
mystring.sayno();//不行 以上是添加了一個實名函數,我們還可以添加一個匿名函數:var mystring = new String('hello world');mystring.sayno=function(){alert('不行');};mystring.sayno();//不行 6、對象語句 如果程式中要使用某個對象的多個屬性和方法,則可以考慮使用with語句,將需要使用其屬性和方法的對象用with語句包含起來,文法:
with(對象){ .....語句.....} 例如:with(document.myform){ if(username.value=='') if(password.value=='')} for語句是一種特殊的迴圈語句,用於遍曆一個對象的所有屬性。注意是所有屬性!
例如,以下樣本用for...in語句遍曆了document對象的所有屬性,並將其顯示出來:document.write('document對象的所有屬性如下:');for(var i in document){ document.write(i+'
');} 二、建立自訂的javascript類和對象 1、工廠方式
所謂工廠方式,就是先建立對象,然後往對象中添加屬性和方法。
帶參批量生產:
function createPerson(name){ var personobj = new Object(); personobj.name=name; personobj.say=function(){ alert('who is'+this.name); } return personobj; } var persion1 = createPerson('fanfan'); var persion2 = createPerson('xiaoxiao'); person1.say();//fanfan person2.say();//xiaoxiao 隨著javascript正規起來,這種建立對象的方式並不提倡了。原因有二: (1)語義上的,即這種方式不適用new運算子建立對象;
(2)功能上的,每次調用createPerson()函數,都建一個新的函數say()。這意味著每個對象都有自己版本的say()方法。然後在現實中,每個對象都是共用相同的方法。
2、建構函式的方式
與工廠方式類似,類名的第一個字母是大寫。
function Person(name){ this.name = name; this.say = function(){ alert('who is'+name); }}var person1 = new Person('fanfan');var person2 = new Person('xiaoxiao'); 我們可以看到,在建構函式內部沒有建立對象,而是使用了this關鍵字。當用new運算子調用建構函式時,在建構函式第一行被執行之前就建立了一個對象,此時這個對象可以通過this運算子訪問。然後,我們可以直接給this分配屬性,而這個this是預設是作為函數值返回的。 然後,與原廠模式一樣,建構函式方式也是沒建立一個對象,就產生一個新函數。
3、原型方式
原型方式利用了對象的prototype屬性。在object類中,每個對象都有一個prototype屬性,該屬性代表對象的父類。
function Person(){ }Person.prototype.name='fanfan'; Person.prototype.say=function(){ alert('who is'+this.name); }
var person1 = new Person();var person2 = new Person();
當new Person()被調用時,所有的prototype的屬性和方法立即被分配給建立的對象,這樣所有的Person執行個體就都包含了指向相同say()函數的指標。從語義上講,屬性和方法都屬於一個對象,這就解決了前面重複產生函數的問題。此外,在這種方式中,我們還可以使用instanceof運算子檢測給定變數所指向的物件類型。alert(person1 instanceof Person);//true
但是,原型方式不能通過建構函式傳遞參數初始化屬性值。
4、混合建構函式和原型方式 使用建構函式方式定義對象的屬性,使用原型方式定義對象的方法。這樣,函數只建立一次,同時每個對象都有自己的對象屬性執行個體。
function Person(name){ this.name=name; } Person.prototype.say=function(){ alert('who is'+this.name); } var person1 = new Person('fanfan'); var persion2 = new Person('xiaoxiao'); alert(person1.say());//fanfan alert(person1.say());//xiaoxiao 5、動態原型方式 function Person(name){ this.name=name; if(typeof Person.initial == 'undefined'){ Person.prototype.say=function(){ alert('who is'+this.name); } Person.initial = true; } } var person1 = new Person('fanfan'); var persion2 = new Person('xiaoxiao'); alert(person1.say());//fanfan alert(person1.say());//xiaoxiao 6、使用JSON格式建立對象 var personobj={ firstname:'fanfan', lastname:'xiaoxiao', age:50, tellyourage:function{ alert('yourage:'+this.age) }} 與工廠方式沒有本質區別,只是更加快捷。
三、對象繼承實現 建立子類將繼承超類的所有屬性和方法,包括建構函式及方法的實現。記住,所有屬性和方法都是公用的,因此子類可直接存取這些方法。子類還可添加超類中沒有的新屬性和方法,也可以覆蓋超類中的屬性和方法。
1、對象冒充
所謂對象冒充,就是新的類冒充舊的類(舊的類必須採用建構函式的方式),從而達到繼承的目的,其原理如下:建構函式使用this關鍵字給所有屬性和方法賦值(即採用類聲明的建構函式方式)。因為建構函式只是一個函數,所以 可使ClassA的建構函式成為ClassB的方法,然後調用它。ClassB就會收到ClassA的建構函式中定義的屬性和方法。
function Person(name){ this.name=name; this.say=function(){ alert('who is'+this.name); } } 我們知道,關鍵字this引用的事建構函式當前建立的對象。而在方法中,this指向的是所屬的對象。這個原理是把People作為常規函數來建立繼承機制,而不是作為建構函式。function WhitePeople(name){ this.inherit=People;//冒充 this.inherit(name);//繼承 delete this.inherit;//刪除繼承 this.area = function(){alert('我在歐洲'); } } var tom=new WhitePeople('tom'); tom.say(); tom.area(); 所以新屬性和新方法必須在刪除了繼承後再定義,這樣是為了避免覆蓋父類的相關屬性和方法。 對象冒充可以支援多繼承,也就是說,一個類可以繼承多個超類。
function Person(name){ this.name=name; this.say=function(){ alert('who is'+this.name); } }function Worker(pay,work){ this.pay=pay; this.work=work;}function City_worker(name,pay,work){ this.inherit = People; this.inherit(name); delete this.inherit; this.inherit = Work; this.inherit(pay,work); delete this.inherit; }var jerry=new City_worker('jerry','10000','coder');jerry.say();alert(jerry.work); 2、call方式 基類.call(對象,參數列表)
function WhitePeople(name){ //this.inherit=People;//冒充 //this.inherit(name);//繼承 //delete this.inherit;//刪除繼承 People.call(this.name); this.area = function(){alert('我在歐洲'); } } var tom=new WhitePeople('tom'); tom.say(); tom.area(); 3、apply apply()也是對象冒充一個封裝函數。apply()方法有兩個參數,用做this的對象和要傳遞給函數的參數的數組。其格式為:
基類.apply(對象,參數數組);
function City_worker(name,pay,work){ People.apply(this,new Array(name)); Worker.apply(this,[pay,work]);}var jerry=new City_worker('jerry','10000','coder');jerry.say();alert(jerry.work);
4、原型鏈 上面三種方式都是採用建構函式的方式繼承,對應的,也具有原型函數方式的繼承,原型鏈。
function Blue_collor(){}Blue_collor.prototype.name='jean';Blue_collor.prototype.say=function(){ alert('who is '+this.name);}function City_blue_collor(){}City_blue_collor.prototype = new Blue_collor();var j1 = new City_blue_collor();j1.say(); 注意在使用原型鏈繼承時,基類建構函式內不能有任何參數。