javascript原型鏈繼承


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

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

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

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

我們來看這樣一個關系圖:

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

 

  通過這個關系圖,我們可以得到以下信息:

  • 實例的__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構造函數的實例。

二、開始說原型鏈繼承:

  現在,有了前面基本概念的了解,我再說關於原型鏈的繼承,首先來看以下代碼:

 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 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM