JavaScript原型,原型鏈 !

js原型

問題:什麼是js原型?
js每聲明一個function,都有prototype原型,prototype原型是函數的一個默認屬性,在函數的建立過程當中由js編譯器自動添加。
也就是說:當生產一個function對象的時候,就有一個原型prototype。
舉個栗子:
是否是還看到了一個_proto_的屬性?!騷年,你的眼睛不錯~待會在解釋prototype和_proto_的基友關係!
prototype的屬性值是一個對象,是 屬性的集合,是屬性的集合,是屬性的集合,重要事情說三遍!
爲何要說他的屬性的集合呢?我再來舉個栗子~:
 
function Person(name,age){ 
    this.name=name; 
    this.age=age; 
} 
    Person.prototype.sayHello=function(){ 
    alert("使用原型獲得Name:"+this.name); 
} 
var per=new Person("alin",21); 
per.sayHello(); //輸出:使用原型獲得Name:alin

   

在函數Person裏面自定義了屬性name和age,而prototype是咱們的屬性集合,也就是說,我要添加sayHello這個屬性到Person,則要這樣寫:Person.prototype.sayHello,就能添加Person的屬性。javascript

(咱們能夠簡單的把prototype看作是一個模板,新建立的自定義對象都是這個模板prototype的一個拷貝,其實準確來講,不該該說是一個拷貝,而是一個鏈接,只不過這種連接是不可見,新實例化的對象內部有一個看不見的_Proto_指針,指向原型對象)。java

 

使用原型來優化代碼:函數

普通code:
function add(x,y){
   return x+y;
}
function subtract(x,y){
   return x-y;
}
console.log(add(1,3));
 

  

第一種方式用原型優化後:
var Calculator = function(){
 
};
Calculator.prototype = {
    add:function(x,y){
     return x+y;
},
subtract:function(x,y){
     return x-y;
}
};
console.log((new Calculator()).add(1,3));

 

第二種方式用原型優化後:
var Calculator = function () {};
Calculator.prototype = function(){
   add = function(x,y){
   return x+y;
},
subtract = function(x,y){
    return x-y;
}
return{
     add:add,
     subtract:subtract
}
}();

console.log((new Calculator()).add(1,3));

  

它目的:封裝私有的function,經過return的形式暴露出簡單的使用名稱,以達到public/private的效果。
 
----------------------------------------------------------------------------------華麗的分割線-------------------------------------------------------------------------------------------

js原型鏈

問題:什麼是原型鏈?
根據《JavaScript高級程序設計》P162頁能夠做出回答:原型鏈是實現繼承的主要方法。其基本思想是:利用原型讓一個引用類型繼承另外一個應用類型的屬性和方法。
簡單回顧一下構造函數、原型和實例的關係:每一個構造函數都有一個原型對象,原型對象都包含一個指向構造函數的指針,而實例都包含一個指向原型對象的內部指針。
 
 
首先,我以爲有必要先解釋一下prototype 和_proto_之間的關係。
每個基本對象都有本身的_proto_屬性,而每個函數對象都有本身的prototype原型(函數對象也屬於基本對象,因此也有_proto_),每當去定義一個prototype的時候,就至關於把該實例的__proto__指向一個結構體,那麼這個被指向結構體就稱爲該實例的原型。 咱們仍是來看圖吧~比較清晰:
var foo = { 
       x: 10, 
       y: 20 
}; 

  

解析:當你定義一個函數對象的時候,其內部就有這樣一個鏈表關係。聲明foo對象,自帶了_proto_的屬性,而這個屬性指向了prototype,從而實現對象的擴展(例如繼承等操做)。優化

 

再看一個栗子:this

var a = { 
      x: 10, 
      calculate: function (z) { 
              return this.x + this.y + z 
       } 
}; 
var b = { 
         y: 20, 
         __proto__: a 
}; 

var c = { 
        y: 30, 
        __proto__: a 
}; 


b.calculate(30); // 60 

  

 

 

附上另外說明:spa

一、一個沒有繼承操做的函數的_proto_都會指向Object.prototype,而Object.prototype都會指向null。prototype

二、因此,也能夠很明顯知道,爲什麼null是原型鏈的終端。設計

理解了__proto__這個屬性連接指針的本質。。再來理解constructor。指針

 

 

 

prototype默認的有一個叫作constructor的屬性,指向這個函數自己。
通常construtor就是咱們平時對函數設置的實例化對象code

如上圖:SuperType是一個函數,下面包括他的原型對象prototype,原型對象指向了構造函數的指針,而構造函數指回像了原型對象的內部指針,這樣就造成了鏈式關係了。
就是說,當一個函數對象被建立時候,Function構造器產生的函數對象會運行相似這樣的一行代碼:
this.prototype = {constructor:this};
這個prototype對象是存放繼承特徵的地方。由於js沒有提供一個方法去肯定哪一個函數是打算用來作構造器,因此每一個函數都會獲得一個prototype對象。constructor屬性沒有什麼用,重要的是prototype對象。


實現原型鏈有一種基本模式,其代碼大體以下:

function A(){
this.Aproperty = "111";
}

A.prototype.getA = function(){
return this.Aproperty;
};

function B(){
this.Bproperty = "222";
}

B.prototype = new A();//B繼承A
B.prototype.getB = function(){
return this.Bproperty;
};

var C = new B();
console.log(C.getA());//111

  

以上定義了兩個類型A和B。每一個類型分別有一個屬性和一個方法。它們的主要區別是B繼承了A,而繼承是經過建立A的實例,並將實例賦給B.prototype實現的。實現的本質是重寫原型的對象,代之以一個新的類型的實例。換句話說,原來存在於A的實例中的全部屬性和方法,如今也存在於B.prototype中了。在確立了繼承關係以後,咱們給B.prototype添加了一個方法,這樣就繼承A的屬性和方法的基礎上又添加了一個新方法。
如圖:

 

另一個很重要的鏈式繼承模式

function A(x){
  this.x = x;
}
A.prototype.a = "a";
function B(x,y){
  this.y = y;
  A.call(this,x);
}
B.prototype.b1 = function(){
  alert("b1");
}
B.prototype = new A();
B.prototype.b2 = function(){
  alert("b2");
}
B.prototype.constructor = B;
var obj = new B(1,3);

  

   就是說把B的原型指向了A的1個實例對象,這個實例對象具備x屬性,爲undefined,還具備a屬性,值爲"a"。因此B原型也具備了這2個屬性(或者說,B和A創建了原型鏈,B是A的下級)。而由於方纔的類繼承,B的實例對象也具備了x屬性,也就是說obj對象有2個同名的x屬性,此時原型屬性x要讓位於實例對象屬性x,因此obj.x是1,而非undefined。第13行又定義了原型方法b2,因此B原型也具備了b2。雖然第9~11行設置了原型方法b1,可是你會發現第12行執行後,B原型再也不具備b1方法,也就是obj.b1是undefined。由於第12行使得B原型指向改變,原來具備b1的原型對象被拋棄,天然就沒有b1了。


  第12行執行完後,B原型(B.prototype)指向了A的實例對象,而A的實例對象的構造器是構造函數A,因此B.prototype.constructor就是構造對象A了(換句話說,A構造了B的原型)。

alert(B.prototype.constructor)出來後就是"function A(x){...}" 。一樣地,obj.constructor也是A構造對象,alert(obj.constructor)出來後就是"function A(x){...}" ,也就是說B.prototype.constructor===obj.constructor(true),可是B.prototype===obj.constructor.prototype(false),由於前者是B的原型,具備成員:x,a,b2,後者是A的原型,具備成員:a。如何修正這個問題呢,就在第16行,將B原型的構造器從新指向了B構造函數,那麼B.prototype===obj.constructor.prototype(true),都具備成員:x,a,b2。

 

  若是沒有第16行,那是否是obj = new B(1,3)會去調用A構造函數實例化呢?答案是否認的,你會發現obj.y=3,因此仍然是調用的B構造函數實例化的。雖然obj.constructor===A(true),可是對於new B()的行爲來講,執行了上面所說的經過構造函數建立實例對象的3個步驟,第一步,建立空對象;第二步,obj.__proto__ === B.prototype,B.prototype是具備x,a,b2成員的,obj.constructor指向了B.prototype.constructor,即構造函數A;第三步,調用的構造函數B去設置和初始化成員,具備了屬性x,y。雖然不加16行不影響obj的屬性,但如上一段說,卻影響obj.constructor和obj.constructor.prototype。因此在使用了原型繼承後,要進行修正的操做。

  關於第十二、16行,總言之,第12行使得B原型繼承了A的原型對象的全部成員,可是也使得B的實例對象的構造器的原型指向了A原型,因此要經過第16行修正這個缺陷。

 

 

 

文章說明:我的查看各類資料,原創所得,觀點不必定準確,歡迎各路大牛勘誤,小女子感激涕零。

相關文章
相關標籤/搜索