JavaScript系列----物件導向的JavaScript

來源:互聯網
上載者:User

標籤:

1.物件導向的編程1.1.什麼是物件導向編程

物件導向編程:即是把能夠完成獨立完成一部分功能的代碼封裝在一起,組成一個類。


  舉個例子來說:

   這裡有一把槍, 槍的種類很多,有步槍,機關槍,阻擊槍....。但是無論怎麼說,這些都是槍的概念,如果把這種抽象的概念剝離出來,就是我們經常說的“類”。那麼槍有什麼特點呢? 威力大小,型號,長度,子彈型號,能承載子彈的數量,槍口半徑......ok! 這些一切的一切都是為了描素槍,把這些抽象出來,就組成了“槍類”的屬性。槍又能幹什麼呢?  瞄準,開火,....這些描素都是槍的功能-----把這些抽象出來,即組成了一個類的方法。

  所以,總體來說,物件導向的編程即是把一個程式模組化,每個模組承載一部分功能,各個模組協同合作,維持程式的正常執行;

  而所謂的類的組成,無外乎三個部分:這個類的名稱(對應著例子中的“槍”),這個類的屬性(對應著特點),這個類的方法(對應著功能)。

就像我們描素一個人一樣,無外乎,描素一個人的特點以及人的能力。所以,現實生活中的人,在程式中也可以抽象成類。

1.2.類,對象,執行個體的關係
  • 類:是一個抽象概念,是對某一類相似對象的抽象。
  • 對象:是類的一個執行個體化,因為類是一個抽象的概念,所以,在使用時必須落實到實物的身上。那麼,對象就作為載體來完成某項功能。
  • 執行個體:和對象是一個概念。一般說一個類的執行個體,指的就是這個類的某個對象。

舉個例子來說明三者之間的關係:

//1.Person是一個類的名字,定義的是一個人,對這個人的描述一般就是姓名,年齡。
var Person = function (name, age) {
//Person類的屬性 this.name = name; this.age = age;}
//Person類的方法Person.prototype.greet = function () { console.log(‘hello‘);}
//這是一個類的執行個體化過程,lisi這裡就是Person類的一個對象,也可以說其是Person類的一個執行個體
var lisi=new Person("lisi",18);
 1.3.物件導向的四個特點
  • 封裝. 所謂的封裝就是把一個類的對象的屬性和方法封裝在類的內部。封裝的好處就是:類與類之間的屬性和方法相互獨立,互不干擾。
  • 繼承. 所謂的繼承就是指的是一個類可以派生自另外一個類。比如,圖形類,可以派生出三角形,正方形,圓....
  • 重載. 重載就是指一個類的方法可以名字可以相同(JS不支援重載).第二部分會給出解釋。
  • 多態.多態指的是父類的方法,子類可以重寫該方法。那麼,子類在調用該方法時調用的會是子類的方法。

請記住:物件導向,所有的一些都是為了代碼的複用。

2.物件導向的四個特點在JS中的實現 2.1.JS中的封裝

      JS類的封裝即是把類的屬性和方法封裝在類的內部. 如果只是簡單的實現封裝,那麼可以有多種方法。比如下面的兩種

//第一種方法
var Person = function (name, age) { this.name = name; this.age = age; this.greet = function () { console.log(‘hello‘); }}
//第二種方法var Person = function (name, age) { this.name = name; this.age = age;}Person.prototype.greet = function () { console.log(‘hello‘);}

 這兩種方法,雖然在使用效果上是一致的,但是在第一種方法中,每次new 一個對象的時候都要為該對象添加一個函數greet----這樣就沒有做到代碼的複用。所以在使用的時候,一般都是使用第二種方式---也就是所謂的組合式建立。所以一般我們也推薦用第二種方式。

2.2.JS中不存在重載

 什麼是重載呢? 重載的概念來源於強型別預言(C++,java,C#)中。我們先來看一些java中的重載

class Person{  //java語言, 定義一個Person類,該類中存在greet方法的重載。    public String name;    public int age;     Person(String name,int age){        this.name=name;        this.age=age;    }           public void greet(){         System.out.println("I am "+ this.name);             }         public void greet(String message){                System.out.println("I am "+ this.name+ "\n This is your"+message);            }}

     所謂的重載,就是一個同一個方法名在一個類中被出現了多次。那麼在該方法被調用的時候,編譯器如何區分具體調用哪個方法呢?

      在強型別語言中,編譯器先根據函數的名字選擇函數,然後在根據調用時,形參和實參的類型,形參的個數和實參的個數是否一致來區分一個函數。

     那麼,問題來了....JS中的解譯器是符合區分一個函數呢? ok...JS中解譯器只是根據函數的名稱來選擇函數,而函數的形參並不在考慮的範圍----因為在編譯時間無法根據確定形參的類型,更無法確定實參的類型。

     既然,JS不支援重載,那麼如果一個函數被重寫了,會出現什麼情況呢?

var Person = function (name, age) {  this.name = name;  this.age = age;  this.greet = function () {    console.log(‘hello‘);  }}var Person = function (name, age) {  this.name = name;  this.age = age;}Person.prototype.greet = function () {  console.log(‘我被覆蓋了‘);}Person.prototype.greet = function (message) {  console.log("我是重寫的方法");}var person=new Person("zhangsan",18);person.greet(); //我是重寫的方法

     根據上面的例子,可以看出,無論函數的參數是什麼,只要函數同名,那麼被調用的肯定是最後一次被寫的同名函數。

2.3.JS中的繼承

  繼承這個概念的來源也是物件導向的編程。JS引薦強型別預言中的繼承做到這一點。所以我們要從強型別語言中的繼承來類推---JS中為什麼要這麼設計。

  2.3.1.強型別語言中繼承的實現

    在強型別語言中,在假設有兩個類 A 、B....A是B的父類。實現如下:

class A{//父類的建構函式    protected int x;    A(int x){        this.x=x;    }}class B extends A{    protected int y;     B(int x,int y){//子類的建構函式         super(x);  //在子類的建構函式中,第一句話總是先調用父類的建構函式,如果不寫 則預設調用super();如果父類中不存在無參建構函式,則編譯時間會報錯。         this.y=y;        }

  public String getPoint(){
    return "("+this.x+","+this.y+")";  //返回座標(x,y)
  }

}

          從上面的這些我們可以看出什麼呢? 就是對象初始化的順序...先初始化父類,在初始化子類。

 初始化的時候順序為: 父類的屬性----》父類的方法-----》子類的屬性-----》子類的方法。(我們這裡講的是排除了類中待用資料和方法來說,因為待用資料和方法的初始化,在類第一次被載入的時候就已經初始化完畢)

     下面我們看下,JS中是怎麼實現和上述一樣的功能的...

var A = function (x) {  this.x = x;}var B = function (x, y) {  A.call(this, x);  //相當於第一種的super()函數。  this.y = y;}
//實現繼承function extend(subClass, superClass) { var prototype = Object.create(superClass); subClass.prototype = prototype; subClass.constructor = subClass;}extend(B, A);B.prototype.getPoint = function () { return ‘(‘ + this.x + ‘,‘ + this.y + ‘)‘;}

    上面這兩段代碼,撇開語言的特性來說,他們實現的功能是等效的。只是第一種玩的是思想,第二種玩的是技巧。

    OK!下面我們開始詳解JS的設計者為了JS語言能實現繼承所做的努力。

2.3.2.JS語言支援繼承的原理。

所有的函數均有一個prototype屬性。就是這個屬性幫我們做到了一些,首先要認識到一點這個屬性是一個對象。

    用上面我們建立的一個Person函數詳解,那麼這個函數的prototype屬性如下表示:

 

    這是這個函數在剛開始被初始化時候的固有形式,後來執行了一句

Person.prototype.greet = function () {  console.log(‘hello‘);}

  在這句執行完畢的時候,Person.prototype變化為

 Person  prototype
constructor 指向Person函數
greet (greet函數)

 

    解釋了這麼多,貌似並沒有解釋繼承是怎麼實現的是吧....別慌...慢慢來!!!

    來看一下,當一個函數被執行個體化的時候發生了什嗎?

var lisi=new Person("lisi",18); //看看Person執行個體化的對象發生了什嗎?

  

 

    到這裡,我們看到了吧..當用new建立一個建構函式的對象的時候。這個對象會有一個【【__proto__】】內建屬性,指向函數的prototype。------這就是對象lisi傳說中的原型對象。

  一個函數只有一個原型(prototype),這個函數在用new調用的時候會把這個原型賦值給當前對象的__proto__屬性。

  當查詢一個對象的屬性的時候,首先查詢對象本身的屬性,如果沒有找到則根據對象__proto__屬性層層向上尋找。

   所以一切的一切都歸咎於,只要們修改一個函數的prototype屬性,那麼就可以實現繼承。

下面圖解,繼承B繼承A的過程。

//1. A類,和B類的建構函式    

var A = function (x) {  this.x = x;}var B = function (x, y) {  A.call(this, x);  //相當於第一種的super()函數。  this.y = y;}

 

//2.修改B的prototype使其繼承A

//實現繼承function extend(subClass, superClass) {  var prototype = Object.create(superClass);  subClass.prototype = prototype;  subClass.constructor = subClass;}extend(B, A);B.prototype.getPoint = function () {  return ‘(‘ + this.x + ‘,‘ + this.y + ‘)‘;}

 

 

如此,便實現B類繼承A類...關鍵點就在函數的prototype屬性上。----在下一篇中會詳解函數的prototype。

 

JS中實現多態

何謂多態?

首先一定要強調一點,只有在繼承存在的情況下才可能出現多態? 這是為什麼呢..因為多態指的是子類覆蓋父類的方法..這種情況就是所謂的多態。

在java中的多態

public class Test {    /**     * @param args     */    public static void main(String[] args) {        // TODO Auto-generated method stub        B b=new B(1,2);        String result=b.getPoint();        System.out.println(result);    }}class A{    protected int x;    A(int x){        this.x=x;    }     public String getPoint(){                  return "我是父類";         }}class B extends A{    protected int y;     B(int x,int y){         super(x);         this.y=y;     }
    //多態,父類覆蓋的方法 public String getPoint(){ return "我是子類"; }}
//輸出結果 :我是子類

 

   在JS中的多態情況,也是指的的子類的方法覆蓋父類的方法。 上面的功能在JS中是這麼實現的。

var A = function (x) {  this.x = x;}A.prototype.getPoint = function () {  return ‘我是子類‘;}var B = function (x, y) {  A.call(this, x); //相當於第一種的super()函數。  this.y = y;}//實現繼承function extend(subClass, superClass) {  var prototype = Object.create(superClass);  subClass.prototype = prototype;  subClass.constructor = subClass;}extend(B, A);B.prototype.getPoint = function () {  return ‘我是子類‘;}var b = new B(1, 2);b.getPoint();
//輸出結果 :我是子類

  在上述代碼執行完畢後,函數B的結構

 

B類在執行個體化的時候,B類的對象會擁有一個內部屬性指向 B.prototype.當該執行個體調用函數的時候,會先在該對象內部查詢該函數是否存在,如果不存在則沿著__proto__屬性查詢原型對象,即B.prototype。如果找到此函數,則停止查詢,否則會接著沿著__proto__屬性所指向的對象,一直找到最上級為止。

 

 

JavaScript系列----物件導向的JavaScript

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.