JavaScript系列----面向對象的JavaScript(1)

1.面向對象的編程

1.1.什麼是面向對象編程

面向對象編程:便是把可以完成獨立完成一部分功能的代碼封裝在一塊兒,組成一個類。javascript


  舉個例子來講:java

   這裏有一把槍, 槍的種類不少,有步槍,機關槍,阻擊槍....。可是不管怎麼說,這些都是槍的概念,若是把這種抽象的概念剝離出來,就是咱們常常說的「類」。那麼槍有什麼特色呢? 威力大小,型號,長度,子彈型號,能承載子彈的數量,槍口半徑......ok! 這些一切的一切都是爲了描素槍,把這些抽象出來,就組成了「槍類」的屬性。槍又能幹什麼呢?  瞄準,開火,....這些描素都是槍的功能-----把這些抽象出來,即組成了一個類的方法。編程

  因此,整體來講,面向對象的編程便是把一個程序模塊化,每一個模塊承載一部分功能,各個模塊協同合做,維持程序的正常執行;模塊化

  而所謂的類的組成,無外乎三個部分:這個類的名稱(對應着例子中的「槍」),這個類的屬性(對應着特色),這個類的方法(對應着功能)。函數

就像咱們描素一我的同樣,無外乎,描素一我的的特色以及人的能力。因此,現實生活中的人,在程序中也能夠抽象成類。this

1.2.類,對象,實例的關係

  • 類:是一個抽象概念,是對某一類類似對象的抽象。
  • 對象:是類的一個實例化,由於類是一個抽象的概念,因此,在使用時必須落實到實物的身上。那麼,對象就做爲載體來完成某項功能。
  • 實例:和對象是一個概念。通常說一個類的實例,指的就是這個類的某個對象。

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

//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不支持重載).第二部分會給出解釋。
  • 多態.多態指的是父類的方法,子類能夠重寫該方法。那麼,子類在調用該方法時調用的會是子類的方法。

請記住:面向對象,全部的一些都是爲了代碼的複用。prototype

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----這樣就沒有作到代碼的複用。因此在使用的時候,通常都是使用第二種方式---也就是所謂的組合式建立。因此通常咱們也推薦用第二種方式。3d

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調用的時候會把這個原型賦值給當前對象的內置屬性。

  當查詢一個對象的屬性的時候,首先查詢對象自己的屬性,若是沒有找到則根據對象內置屬性層層向上查找。

   因此一切的一切都歸咎於,只要們修改一個函數的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的prototype

//實現繼承
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.當該實例調用函數的時候,會先在該對象內部查詢該函數是否存在,若是不存在則沿着內置屬性查詢原型對象,即B.prototype。若是找到此屬性,則中止查詢,不然會接着沿着內置屬性所指向的對象,一直找到最上級爲止。

相關文章
相關標籤/搜索