JS 原型鏈

ForTechnology發表於2011-08-05

  之前對JS的prototype知識比較模糊,今天理清了記下來,以防忘記,直切正題:

  

  1.要明白原型鏈,就必須先清楚JS的建構函式模式:

    js是物件導向的語言,既然是面型物件,就一定會有一個物件的模板,Java中用"類"來作為物件的模板,而JS中,可以用建構函式來作為物件的模板,你可以認為相當於Java中的"類",

  寫法如下所示:

  function Cat(name,color){
       this.name=name;
       this.color=color;
   }
  var c = new Cat();

 

  Cat函式就是建構函式,建構函式中的this指向的就是當指令碼執行時Cat所生成的例項,如果把建構函式看成類,就會輕鬆許多。

  緊接著下面一句var c = new Cat();就是根據建構函式生成相應的物件,類似於JAVA中根據類生成例項物件一樣。

  這就是JS中物件導向的構造器模式。

 

  2.明白了建構函式模式,來看看建構函式模式的弊端以及如何解決:

    我們可以根據建構函式來建立N個物件,每個物件有自己的記憶體空間。我們來思考這麼一個問題:如果建構函式中有兩個固定值的屬性,當我們用這個建構函式去建立物件的時候,每個物件都會在自己的記憶體空間中存放這個固定值的屬性,這就造成了不必要的浪費,對吧?想一想JAVA是不是也存在這種情況?是存在的,那JAVA怎麼解決的?對,繼承!

    在JAVA中我們會將一些物件存在共性的地方,抽取出來存放到Super類中;在JS中對於每一個建構函式都有這麼一個額外的物件,用來存放一些共有的東西,是不是很父類很像?這個額外的物件就是原型物件,一個原型物件對應一個建構函式,也就是說一個建構函式只有一個原型物件,例如建構函式Object,就對應一個原型物件,建構函式Obejct的prototype屬性指向他所對應的原型物件,而Object構造器生成的例項物件都有一個__proto__屬性,也只想Obejct構造器所對應得原型物件,這樣就實現了節省記憶體的目的。如下程式碼:

  function Cat(name,color){
    this.name = name;
    this.color = color;
  }
  Cat.prototype.type = "貓科動物";
  Cat.prototype.eat = function(){alert("吃老鼠")};

  3.明白了原型的概念,再來看看原型鏈的概念:

    要清楚在建構函式模式中有幾個角色:建構函式、例項物件、原型物件。 在這三個物件之間,他們的關係是怎樣的?先來理一理。

    

    如上圖所示,A代表建構函式,它持有一個prototype屬性,指向他所對應的原型物件A.prototype,而A.prototype持有constructor屬性指向A建構函式,以此來形成一對一的關係,而new A()代表著根據A建構函式來生成的例項物件,new A()持有一個__proto__屬性,指向A所對應的原型物件,以此來實現多個例項物件共享原型物件中的固定狀態的某些變數。

    所以總結一下:建構函式中有一個prototype屬性、原型物件有一個constructor屬性、例項物件中有一個__proto__屬性。

    剛才好像落下一個,就是原型物件中除了constructor屬性外,其實還有一個__proto__屬性,這個怎麼理解?如果你把原型物件看成一個例項物件,是不是他也可以從其他的原型物件中共享資料?這就是了,原型物件中的__proto__就是構成原型鏈的關鍵,你可以理解成繼承鏈。

    我們都知道JS是物件導向,那就不可避免的JS存在一個上帝級別的物件,就是Obejct,所有的一切物件來源於它,什麼意思呢?

    其實上面的A構造器所對應的原型物件中的__proto__指向就是Object的原型物件,所以可以向相面的圖這樣理解:

    

     補充一點:其實建構函式其實也是一個物件對吧?那麼它是那個建構函式構造出來的呢?當然是Function,所以說建構函式作為一個函式例項物件,也持有__proto__屬性,並指向Function.prototype。

參考資料

 

相關文章