面向對象編程(OOP)的主要原則就是使用class來建立一個類,這個類以某種方式實現並知足繼承,多態和封裝。在javascript的OOP中,沒有類的概念,只有對象。對象必須能封裝一些功能並繼承方法和屬性,在javasript中咱們僅僅只關心繼承和封裝。javascript
正如你已經知道,javascript中到處都是對象。咱們使用對象字面量和構造函數來建立對象。一樣,你必需要知道的是在javascript中函數也是對象。java
封裝就是把一個對象的全部功能包裹起來,這樣對象的內部工做(它是如何構建-它的方法和屬性)相對於後面的應用就隱藏了。這就使得咱們可以從其餘的應用中抽象(隔離)出指定對象的特殊功能集合。繼承就是一個對象從父對象中繼承方法和屬性。編程
封裝和繼承這兩個概念是很重要的,由於他們使得咱們經過代碼重用,可擴展結構和抽象化來構建應用。這樣就使得應用是很容易維護的,高效的和可擴展的。數組
javascript中的OOP,它最重要的兩個原則就是代碼建立模式(封裝)和代碼重用模式(繼承)。當咱們建立應用時,你會構建不少對象。建立這些對象的方式有不少:你可使用最基本的對象字面量方式,例如:app
1 var myObj={name:"deng",profession:"developer"};
你也可使用構造函數:函數
1 function People(name,profession){}//People()是一個構造函數,咱們將使用new關鍵字來調用它 2 var deng=new People("deng","developer")//deng是一個經過構造函數新建的對象
當咱們想要給本身的對象添加屬性和方法時,當咱們想要封裝對象的功能時,咱們該如何來建立對象?當你建立代碼時。咱們該如何從父對象中繼承大部分方法和屬性(有類似功能),並且他們要可以擁有本身的屬性和方法?測試
javascript中的封裝this |
爲了實際的使用javascript中的OOP,咱們將會使用在本文學到的方法和原則來建立一個面向對象的考試應用。咱們的考試應用將會有一個user(user構造函數)來執行測試。執行測試的每個user都會有許多相同的屬性:每個user都會有name,score,email。這些都是user對象的屬性。此外,每個對象應該能顯示姓名,保存分數,變動郵件,這些都是對象的方法。由於,咱們但願全部的user對象擁有這些屬性和方法。 spa
OOP的封裝—把一個對象的內部運行方式放在對象中。爲了實現javascript中的封裝,咱們必須定義這個對象的核心屬性和方法。要想作到這些咱們使用javascript中最好的模式:構造函數和原型的聯合模式。prototype
構造函數和原型模式的具體實現:
1 function User(name,email){ 2 this.name=name; 3 this.email=email; 4 this.scores=[]; 6 } 7 User.prototype={ 9 showScore:function(theScoreToAdd){ 10 this.scores.push(theScoreToAdd) 11 }, 12 showName:function(){ 14 return this.name; 15 }, 16 changeEmail:function(newEmail){ 17 this.emai=newEmail; 18 return "new email is:"+this.email; 19 } 20 21 }
建立user函數的實例:
1 1 // a User 2 2 firstUser=new user("deng","5864123@qq.com"); 3 3 firstUser.changeEmail("deng@163.com"); 4 4 firstUser.showScore(15); 5 5 firstUser.showScore(10); 6 6 firstUser.showNameAndScores();//deng scores:15,10
正如你看到的,咱們在user函數中封裝了User的全部功能。這樣User的每個實例都能使用原型中的方法(像changeEmail)並定義他們的實例屬性(name和email)
javascript中的繼承 |
繼承的實現容許咱們從父函數中繼承功能,這樣咱們就能很容易的重用代碼並擴展對象的功能。對象既能使用繼承的功能也能擁有他們特定功能。在javascript中實現繼承的最好模式莫過於構造函數和原型鏈的組合。在討論繼承的同時咱們順便講解一下javascript中基於函數的面向對象成員和方法的訪問域問題(也就是共有,私有,特權)
首先來看看javascript是如何實現這些訪問域問題的:
具體的代碼以下:
1 function Animal(name){ 2 this.name=name;//共有屬性 3 var that=this;//私有屬性 4 function alertMessage(){//私有方法 5 alert(that.name); 6 } 7 alertMessage() 8 this.appendAge=function(age){//特權方法 9 this.name+=":"+age; 10 alertMessage(); 11 } 12 } 13 Animal.prototype.getName=function(){//共有方法 14 alert(this.name); 15 } 16 Animal.gender=male;//靜態屬性 17 Animal.getGender=function(){//靜態方法 18 alert(this.gender); 19 }
下面咱們來看看javascript是如何使用構造函數和原型鏈的組合來實現繼承的
首先利用構造函數來構建一個父對象,並在其原型上添加方法:
1 function Animal(name){ 2 this.name=name;//共有屬性 3 } 4 Animal.prototype.getName(){//共有方法 5 alert(this.name); 6 }
接着構建子對象,繼承父對象的屬性和方法並添加本身的屬性和方法:
1 function Dog(name,color){ 2 Animal.call(this,name);//繼承屬性 3 this.color=color;//本身的屬性 4 } 5 Dog.prototype=new Animal();//繼承方法 6 Dog.prototype.constructor=Dog; 7 Dog.prototype.getColor=function(){//構建本身的方法 8 alert(this.color); 9 }
最後實例化子對象,調用父對象和本身的方法:
1 var dog1=new Dog("xiaohua","black"); 2 dog1.getName();//xiaohua 調用父對象方法 3 dog1.getColor();//black 調用自身方法
在這裏咱們不得不解釋一下爲何要使用構造函數和原型相結合的方式?而不是單獨使用構造函數或者原型鏈模式。
對於構造函數模式,全部的屬性寫在構造函數裏面的同時,全部的方法也都寫死在構造函數中,這樣就沒法抽象複用和重構這些方法了。
對於原型鏈模式,若果構建兩個實例,那麼兩個實例都會共享父對象原型中的引用類型(如數組)。一個實例改變這個引用類型的值後會影響另外一個實例。而實際上咱們但願實例是獨立的互不干擾的。此外不能向父對象的構造函數中傳遞參數,從而不能繼承構造函數中的屬性。
構造函數和原型鏈的組合模式,把共有屬性寫在構造函數裏,把共有方法寫在原型上。利用構造函數模式能夠實現對父對象構造函數中屬性的繼承,利用原型鏈能夠繼承原型中的方法和屬性,實現代碼複用。