對象:一切事物皆是對象。對象是一個整體,對外提供一些操作。比如說一個收音機是一個對象,我們不需要知道它的內部結構是什麼,只需要會使用外部的按鈕就可以使用收音機。 面向對象:面向對象語言的標誌是他們都有類的概念,通過類可以創建任意多個具有相同屬性的方法的對象。任何對象都是某一類事物的實例,簡單的說就是 ...
對象:一切事物皆是對象。對象是一個整體,對外提供一些操作。比如說一個收音機是一個對象,我們不需要知道它的內部結構是什麼,只需要會使用外部的按鈕就可以使用收音機。
面向對象:面向對象語言的標誌是他們都有類的概念,通過類可以創建任意多個具有相同屬性的方法的對象。任何對象都是某一類事物的實例,簡單的說就是使用對象時,只關註對象提供的功能,不關註其內部細節。
面向對象的特點:封裝,繼承,多態。
JS中面向對象的組成:1.方法(函數):過程,動態的 2.屬性(有所屬關係的變數):狀態,靜態的
對象是無序屬性的集合,其屬性可以包括基本值、函數、對象。每個屬性都會有一個名字,每個名字映射到一個值上
下麵是創建一個對象的一些方法
1.工廠模式
1 function createPerson (name,sex) {//工廠方式構造一個對象 2 3 //1.原料 4 var obj=new Object(); //new一個空白對象 5 6 //2.加工 7 obj.name=name; 8 obj.sex=sex; 9 10 obj.showName=function(){ 11 console.log('我的名字叫:'+this.name) 12 } 13 14 obj.showSex=function(){ 15 console.log('我是:'+this.sex+'的') 16 } 17 18 //3.出廠 19 return obj;//這一步千萬不能那下 20 } 21 22 var p1=createPerson('木木','女');//每用一次createPerson 就會new一個對象,每個對象都有一套自己的方法 造成資源浪費 23 var p2=createPerson('揚揚','男'); 24 25 p1.showName(); 26 p1.showSex(); 27 p2.showName(); 28 p2.showSex();
使用工廠方式構造對象步驟:
1,原料
2.加工
3.出廠
工廠方式不常用,因為有缺點:1.沒有new 2.每個對象都有一套自己的函數,造成資源的浪費
怎麼解決這個兩個問題呢?那麼請看下麵
2.構造函數模式
1 function Person (name,sex) {//構造函數 構造一個對象 2 this.name=name;//this:當前的方法屬於誰(在函數前面有new時會失效) 3 this.sex=sex; 4 5 this.showName=function(){ 6 console.log('我的名字叫:'+this.name); 7 } 8 9 this.showSex=function(){ 10 console.log('我是:'+this.sex+'的'); 11 } 12 13 } 14 15 var p1=new Person('木木','女'); //外面加new後Person函數裡面就不用new一個空白對象 16 var p2=new Person('揚揚','男'); 17 18 console.log(p1.showName==p2.showName);//fase 19 20 p1.showName(); 21 p1.showSex(); 22 p2.showName(); 23 p2.showSex();
註意:一般將Person稱為構造函數,並且構造函數的名字首字母大寫,這是編碼規範。
this:表示當前的方法屬於誰,但是在這裡當this碰到New時就會失效。
用造函數模式解決了上面工廠模式沒有New的問題
這時就要想一想了在使用new操作符調用構造函數創建一個新實例的過程中發生了什麼呢?一個新實例是怎麼生成的呢?
這種方式調用構造函數經歷以下四個步驟:
1.創建一個新對象
2.將構造函數的作用域賦給新對象(因此this就指向了這個新對象)
3.執行構造函數中的代碼(為這個新對象添加屬性)
4.返回新對象
1 function show(){ 2 alert(this); 3 } 4 show();//彈出window對象(當在全局作用域中調用一個函數時,this對象總指向window對象,全局函數屬於window的一個方法) 5 new show();//彈出obj (this會指向)新創建的一個對象
面這個例子會看得更加清楚
作為普通函數調用:
1 var name = "mumu"; 2 function Person(name){ 3 this.name = name; 4 this.show = function(){ 5 console.log("我是" + this.name); 6 } 7 } 8 9 Person("javascript"); 10 console.log(name);//結果是javascript
全局變數name被修改了
作為構造函數調用:
1 var name = "mumu"; 2 function Person(name){ 3 this.name = name; 4 this.show = function(){ 5 console.log("我是" + this.name); 6 } 7 } 8 9 var Name = new Person("HTML"); 10 console.log(Name.name); 11 console.log(name);
this指向新對象Name,全局變數name也沒有變化
那麼問題又來了:
1 console.log(p1.showName==p2.showName);//fase
不同實例的showName()函數是不相同的,那麼怎麼解決這個問題呢?下麵是一個解決辦法
1 function Person (name,sex) {//構造函數 2 this.name=name;//this:當前的方法屬於誰(在函數前面有new時會失效) 3 this.sex=sex; 4 5 this.showName=showName; 6 7 } 8 function showName(){ 9 console.log('我的名字叫:'+this.name+'我是:'+this.sex+'的'); 10 } 11 12 var p1=new Person('木木','女'); //外面加new後Person函數裡面就不用new一個空白對象 13 var p2=new Person('揚揚','男'); 14 15 console.log(p1.showName==p2.showName);//ture 16 17 p1.showName(); 18 p2.showName();
將showName定義成一個全局方法,這樣每個實例共用的都是全局方法showName()。不相等的問題是解決了,可是如果構造函數里有大量的方法,這就造成代碼中有大量的全局變數,這樣我們自定義的引用類型就沒有封裝性了,資源還是照樣浪費。那麼怎麼解決這個問題呢?請看原型模式
3.原型模式
1 function Person(name,sex) {//原型模式構造函數 2 Person.prototype.name=name; 3 Person.prototype.sex=sex; 4 Person.prototype.showName=function(){ 5 console.log('我的名字叫:'+this.name+'我是:'+this.sex+'的'); 6 } 7 } 8 9 var p1=new Person('木木','女'); 10 var p2=new Person('揚揚','男'); 11 12 console.log(p1.showName==p2.showName);//ture 13 14 p1.showName(); 15 p2.showName();
prototype(原型)返回對象類型原型的引用。這個屬性是一個指針,指向對象。可以讓所有對象實例共用它所包含的屬性和方法,可以擴展系統對象,節省系統資源,所以這裡解決了上面資源浪費問題。
原型的問題:是當一個實例改變屬性值時,所有實例對應的屬性值也都跟著改變,無法初始化屬性值,當為對象實例添加一個屬性時,這個屬性就會屏蔽原型對象中保存的同名屬性,下麵是個小例子:
1 function Person(name,sex) {//原型模式構造函數 2 Person.prototype.name=name; 3 Person.prototype.sex=sex; 4 Person.prototype.showName=function(){ 5 console.log('我的名字叫:'+this.name+'我是:'+this.sex+'的'); 6 } 7 } 8 9 var p1=new Person('木木','女'); 10 var p2=new Person('揚揚','男'); 11 12 p1.name="兮兮"; 13 14 p1.showName();//兮兮 來自實例 15 p2.showName();//揚揚 來自原型
前面幾種方法都各有各優缺點,那麼把它們綜合一下又會怎麼樣呢?
4.組合使用構造函數模式和原型模式
1 //構造函數模式定義實例屬性 2 function Person (name,sex) { 3 this.name=name; 4 this.sex=sex; 5 } 6 7 //原型模式共用方法和共用屬性 8 Person.prototype.showName=function(){ 9 console.log('我的名字叫:'+this.name+'我是'+this.sex+'的') 10 } 11 12 var p1=new Person('木木','女'); 13 14 p1.showName();
這種方法是最常用的,結合了兩種方法的優點,最大限度地節省了記憶體
5.動態原型模式
1 function Person(name, age) { 2 this.name = name; 3 this.age = age; 4 5 //方法 6 if(typeof this.showName != 'function') { 7 Person.prototype.showname = function() { 8 console.log("我的名字是: " + this.name); 9 } 10 } 11 } 12 var person1 = new Person("mumu", 17); 13 person1.showname();
動態原型方法可以通過檢查方法是否有效,決定初始化的原型。這種方法堪稱為完美,但是不能使用面向字面量重寫原型,這樣會切斷現有實例與新原型之間的聯繫。
6.寄生構造函數模式
1 function createPerson(name, age) { 2 var obj=new Object(); 3 obj.name = name; 4 obj.age = age; 5 obj.showName = function() { 6 console.log("我的名字是:" + this.name); 7 } 8 return obj; 9 } 10 var person = new createPerson("mumu", 17); 11 person.showName();
此方法可以和工廠模式對比一下,創建實例方法與構造函數相同,其餘與工廠模式相同。如果前面方法都不適用可以考慮一下這種方法,但是這種方法構造函數返回的對象與實例沒有關係,也不能依賴instanceof判斷對象類型,因此,如果可以使用其他模式這種方法建議還是不要使用。
7.穩妥構造函數模式
1 function Person(name, age) { 2 var obj = new Object(); 3 //定義一些私有變數和函數 4 obj.showName = function() { 5 console.log("我的名字是:" + name); //定義的私有變數等只能通過showName訪問 6 } 7 return obj; 8 } 9 var person1 = Person("兮兮", 17); 10 person1.showName();
所謂穩妥對象,指的是沒有公共屬性。這種方法適合用於在安全的環境下,因為它不使用new調用構造函數,也不使用this引用實例方法。若想訪問其屬性,只能通過showname方法來訪問其內部私有屬性。