javascript面向對象編程筆記

對象:一切事物皆是對象。對象是一個整體,對外提供一些操作。比如說一個收音機是一個對象,我們不需要知道它的內部結構是什麼,只需要會使用外部的按鈕就可以使用收音機。

 

面向對象:面嚮對象語言的標誌是他們都有類的概念,通過類可以創建任意多個具有相同屬性的方法的對象。任何對象都是某一類事物的實例,簡單的說就是使用對象時,只關注對象提供的功能,不關注其內部細節。

 

面向對象的特點:封裝,繼承,多態。

 

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);//fase19 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);//ture16 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);//ture13     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方法來訪問其內部私有屬性。


解釋型語言

一般來說,我們平常用到的大部分語言爲分解釋型語言編譯型語言

還不太清楚這兩種語言的同學可以看看下面的例子:

我們把一個程序的運行理解爲聽一個人演講,聽衆是計算機,演講人是程序員。他們使用不同的語言,讓計算機直接這麼聽演講,它肯定是聽不懂的,於是我們有兩個方案:

1、找人同步翻譯

我們找翻譯能力超強的人作爲翻譯官。每當我們說完一句話,翻譯官立刻跟聽演講的計算機說“剛纔他那句話的意思是………………”。

2、預先翻譯好講話稿

我們的演講稿必須事先寫好,然後讓翻譯官將我們寫好的演講稿翻譯成指定的語言。等到真正演講大會的時候,隨便找個口語好的人讀一下翻譯後的稿子就可以了。


這兩個方案分別就是解釋型語言編譯型語言了。前者每讀一行代碼執行一行代碼,後者需要有一個編譯的過程,將我們的代碼編譯成計算機語言再執行。

JavaScript是一種解釋型語言解釋型語言不需要編譯也能立刻執行代碼,非常適合用於變動性大的場景,比如瀏覽器,我們可以在任意一個時間將head中添加一個新的js引用,由於是解釋型語言,瀏覽器能夠立刻解釋這段腳本並執行。

一般來說,從運行效率上講,編譯型語言是非常有優勢的,就像已經把文章翻譯好了一樣,只需要不動腦子地去讀就行了。

但是從靈活性上講,解釋型語言有着編譯型語言無法超越的優勢,只要有一個解析器,不需要任何翻譯工作,就可以隨時隨地的執行任何語句。

面向對象

JavaScript在設計之初就是一種面向對象的語言,雖然我們在日常的開發中很少能體驗到這一點。面嚮對象語言往往有個特徵就是需要new一個實例。而JavaScript也確實如此:

  1.    var dNow = new Date();

  2.    var obj = new Object();

面向對象擁有三大特徵

  1. 封裝

  2. 繼承

  3. 多態

JavaScript作爲面向對象的語言,也實現了這三大特徵,在本課程的其它章節中,會對它們進行逐一介紹。

基於原型

JavaScript是一款基於原型模式的面嚮對象語言。

我們可以把原型理解爲是一個使用說明書,每一個類型都會有一個對應的使用說明書,凡是使用說明上規定的成員,都是可以使用的。

比如電視器的說明書上規定了開機、關機、換臺等行爲,那麼每一臺電視機都會具備這些功能。

並且我們還可以通過JavaScript代碼爲指定的類型的使用說明添加新的成員。

原型是JavaScript面向對象開發中最重要的一個概念。 關於原型的詳細說明也會在本課程的其它章節中出現。

原始對象

我們都知道面向對象中最重要的環節是封裝。JavaScript提供了定義一個原始對象的方法,詳細代碼請見右側示例:

示例中我們發現,通過構建一個Object實例,我們可以爲該實例手動添加任何成員,可以是字符、數字、布爾甚至於一個方法,定義的方式,即可以用實例名.成員名 = 內容,也可以使用實例名["成員名"] = 內容

  1.    worker.isWorking = false;        //通過明確的屬性名添加成員

  2.  

  3.    var str = "phone";

  4.    worker[str] = 13800000000;    //通過不明確的屬性名添加成員(str可變,因此不明確)

  5.  

  6.    var phone = worker.phone;    //通過不明確添加的成員,可以進行明確的訪問;也可以通過不明確的方式訪問明確的成員。

  7.    var name = worker["name"];

從上面的代碼我們可以發現,明確訪問成員和不明確訪問成員,二者的效果是一樣。


另外,JavaScript也提供了更快捷的定義一個原始對象的方法:

  1.    var worker = {

  2.        name : "John",

  3.        age : 30,

  4.        isWorking : false,

  5.        startWork : function(){

  6.            if(!this.isWorking){

  7.                this.isWorking = true;

  8.            }

  9.        }

  10.    };

通過上面的方法,我們可以定義一個對象,將對象上的屬性定義後便完成了封裝的工作。之後,我們只需要調用對象上的成員,就可以相應的操作了。




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