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
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
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
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高級程序設計》
=============================================================================================