javascript原型鏈繼承

1、關於javascript原型的基本概念:javascript

prototype屬性:每一個函數都一個prototype屬性,這個屬性指向函數的原型對象。原型對象主要用於共享實例中所包含的的屬性和方法。html

constructor屬性:每一個原型對象都有一個constructor屬性,這個constructor屬性包含一個指向prototype屬性所在函數的指針。 例如 Foo.prototype.constructor指向Foo函數。這個屬性是隻讀的。java

__proto__屬性(ES6經過對__proto__屬性進行標準化):建立一個構造函數的實例後,這個實例包含一個指針,指向這個構造函數的原型對象,這個指針叫作[[Prototype]],在chrome、Safari、Firefox下可經過對象的__proto__屬性訪問。__proto__屬性是實例才擁有的,在javascript中函數也是對象,它是Function類型的實例, 因此函數也有__proto__屬性。chrome

咱們來看這樣一個關係圖:函數

    (圖片來自:http://www.mollypages.org/misc/js.mp)this

 

  經過這個關係圖,咱們能夠獲得如下信息:spa

  • 實例的__proto__屬性始終指向它構造函數的原型對象:
1 function Foo(){}; 2 var foo=new Foo(); 3 console.log(foo.__proto__===Foo.prototype); //true
4 console.log(Foo.__proto__===Foo.constructor.prototype);//ture

 

  • 全部對象的原型最終都會指向Object對象的原型:
1 function Foo(){}; 2 var foo=new Foo(); 3 console.log(foo.__proto__); //指向Foo構造函數的原型
4 console.log(foo.__proto__.__proto__); //指向Object構造函數的原型
5 console.log(foo.__proto__.__proto__.__proto__); //null
  • 用戶自定義函數的__proto__屬性指向Function.prototype,因此它們都是Function構造函數的實例。

2、開始說原型鏈繼承:prototype

  如今,有了前面基本概念的瞭解,我再說關於原型鏈的繼承,首先來看如下代碼:設計

 1 function Foo(name){  2     this.name=name;  3 }  4 Foo.prototype.myName=function(){  5         console.log("My name is "+this.name);  6 }  7 
 8 function Bar(name){  9      this.name=name; 10 } 11 Bar.prototype=new Foo(); //Bar的原型指向Foo的實例
12 var bar=new Bar("Tom"); 13 bar.myName();//輸出:」my name is Tom「

  以上代碼實現了Bar構造函數繼承了Foo構造函數的myName()方法。繼承是經過原型鏈的查找來實現的,原型鏈的查找規則是:當實例調用一個屬性(或方法)時,首先會在這個實例中查找該屬性(或方法),在沒有找到的狀況下,會繼續在這個實例的原型上查找,若是依舊找不到這個屬性(或方法),搜索過程會沿着原型鏈向上查找,直到原型鏈的末端,整個查找過程纔會中止。上面例子的原型鏈關係如圖:指針

 

     例子中當實例bar調用myName()方法時,會首先在bar這個實例中查找有沒有myName()這個方法,若是沒有找到,則會在Bar.prototype上查找,在Bar.prototype上沒有找到的狀況下,繼續在Foo.prototype上查找,結果在Foo.prototype上找到了該方法。

   爲什經過 「Bar.prototype=new Foo()」 可以實現這樣有繼承關係的原型鏈呢?咱們能夠經過如下代碼來驗證:

 1 function Foo(name){  2     this.name=name;  3 }  4 Foo.prototype.myName=function(){  5         console.log("my name is "+this.name);  6 }  7 
 8 function Bar(name){  9      this.name=name; 10 } 11 
12 var foo=new Foo(); 13 Bar.prototype=foo; 14 var bar=new Bar(); 15 bar.__proto__===Bar.prototype;  //返回true
16 bar.__proto__===foo; //返回true
17 bar.__proto__.__proto__===foo.__proto__; //返回true
18 foo.__proto__===Foo.prototype; //返回true,證實了Bar 繼承了Foo

   或許你會問經過「Bar.prototype=new Foo()」可以實現繼承,爲何不是「Bar.prototype=Foo.prototype」呢?好的,下面先看這段簡單的代碼:  

1 a={a:1,b:2}; 2 b=a; 3 b.c=3; 4 console.log(a.c); //3 ,a和b是引用類型

  看完這段代碼,咱們想象一下,Bar.prototype  和Foo.prototype也是Object類型的,因此它們之間相互賦值,將指向相同數據地址,若修改Bar原型上的屬性,Foo原型上的同名屬性也會被修改,來看看下面的示例:

 1 function Foo(){}  2 Foo.prototype.name="Tom";  3 Foo.prototype.sayName=function(){  4  console.log("My name is " +this.name);  5 }  6 var foo=new Foo();  7 foo.sayName();//"My name is Tom"
 8 function Bar(){}  9 
10 Bar.prototype=Foo.prototype; 11 Bar.prototype.name="Jerry"; 12 var bar=new Bar(); 13 bar.sayName();//"My name is Jerry",也能Foo原型上的方法
14 foo.sayName();//"My name is Jerry",Foo原型上的name屬性已經被修改

  經過上面的示例咱們能夠看到,當「Bar.prototype=Foo.prototype」時,若是給Bar.prototype上添加一個name屬性,Foo.prototype上name屬性也將隨之被修改,咱們知道繼承的目的就是子函數(子類)複用、重寫、擴展父函數(父類)的方法,但繼承決不容許改變父函數(父類)的方法和屬性 ,因此Bar.prototype=Foo.prototype是不能實現繼承的。
  那麼讓「Bar.prototype=Foo」能不能實現繼承呢?咱們來用代碼驗證一下:

1 function Foo(){} 2 function Bar(){} 3 Bar.prototype=Foo; 4 var bar=new Bar(); 5 bar.__proto__===Bar.prototype; //true
6 bar.__proto__===Foo; //true
7 Foo.__proto__===Function.prototype; //ture
8 Function.prototype.__proto__===Object.prototype; //ture
9 Object.prototype.__proto__===null; //ture

  經過bar.__proto__向上查找不到Foo.prototype,因此Bar構造函數是不能繼承Foo構造函數的。

 

參考資料:《javascript高級程序設計》

=============================================================================================

本文地址:http://www.cnblogs.com/liubaozhe/p/4618989.html 

相關文章
相關標籤/搜索