困擾了本人很久的原型,很讓人頭疼,苦心鑽研後,總結以下:javascript
下面理解下:java
須要記住幾點: 記住!!!記住!!!記住!!!(重要的說3遍)函數
1. 實例的_proto_屬性指向構造函數的原型this
2. Function的構造函數是它本身spa
3._proto_和contructor屬性是對象所獨有的prototype
4.prototype屬性是函數所獨有的,由於函數也是一種對象,因此函數也擁有_proto_和contructor屬性設計
如今正式開始!讓咱們從以下一個簡單的例子展開討論,並配以相關的圖幫助理解:指針
function Foo() {...};code
let f1 = new Foo();orm
以上代碼表示建立一個構造函數Foo(),並用new關鍵字實例化該構造函數獲得一個實例化對象f1。雖然是簡簡單單的兩行代碼,然而它們背後的關係倒是錯綜複雜的,以下圖所示:
重要的有兩點:
1、 建立一個function的過程
在 Javascript 語言中,constructor 屬性是專門爲 function 而設計的,它存在於每個 function 的prototype 屬性中。這個 constructor 保存了指向 function 的一個引用。
在定義一個函數(代碼以下所示)時,
function F() {
// some code
}
JavaScript 內部會執行以下幾個動做:
這樣當咱們把函數 F 做爲自定義構造函數來建立對象的時候,對象實例內部會自動保存一個指向其構造函數(這裏就是咱們的自定義構造函數 F)的 prototype 對象的一個屬性proto,
因此咱們在每個對象實例中就能夠訪問構造函數的 prototype 全部擁有的所有屬性和方法,就好像它們是實例本身的同樣。固然該實例也有一個 constructor屬性了(從 prototype 那裏得到的),每個對象實例均可以經過 construtor 對象訪問它的構造函數
2、new的理解
按照《悟透javascript》書中說的,new形式建立對象的過程實際上能夠分爲三步:
第一步是創建一個新對象(叫A吧);
第二步將該對象(A)內置的原型對象設置爲構造函數(就是Person)prototype 屬性引用的那個原型對象;
第三步就是將該對象(A)做爲this 參數調用構造函數(就是Person),完成成員設置等初始化工做。
其中第二步中出現了一個新名詞就是內置的原型對象,注意這個新名詞跟prototype對象不是一回事,爲了區別我叫它inobj,inobj就指向了函數Person的prototype對象。在person的prototype對象中出現的任何屬性或者函數均可以在one對象中直接使用,這個就是javascript中的原型繼承了。
又頭暈了,上圖吧!
這樣one對象經過內置的原型對象inobj就能夠直接訪問Person的prototype對象中的任何屬性與方法了。這也就解釋了上面的代碼中爲何one能夠訪問form函數了。由於prototype對象中有一個constructor屬性,那麼one也能夠直接訪問constructor屬性。
new操做符具體幹了什麼呢?其實很簡單,就幹了三件事情。
var obj = {};
obj.__proto__ = Base.prototype;
Base.call(obj);
第一行,咱們建立了一個空對象obj
第二行,咱們將這個空對象的__proto__成員指向了Base函數對象prototype成員對象
第三行,咱們將Base函數對象的this指針替換成obj,而後再調用Base函數,因而咱們就給obj對象賦值了一個id成員變量,這個成員變量的值是」base」,關於call函數的用法。
代碼:
function Person(name)
{
this.name=name;
this.showMe=function()
{
alert(this.name);
}
};
Person.prototype.from=function()
{
alert('I come from prototype.');
}
var one=new Person('js');
one.showMe();//js,這個結果沒有什麼好奇怪的
one.from();//I come from prototype.,這個結果有一點奇怪吧
alert(one.constructor);//function Person(name) {...}
alert(Person.prototype.constructor);//function Person(name) {...}
再看看繼承是如何實現的
function Person(name)
{
this.name=name;
this.showMe=function()
{
alert(this.name);
}
};
Person.prototype.from=function()
{
alert('I come from prototype.');
}
function SubPerson()
{
}
SubPerson.prototype=new Person();
var subOne=new SubPerson();
subOne.from();//I come from prototype.
alert(subOne.constructor);//function Person(name) {...};
alert(SubPerson.prototype.constructor);//function Person(name) {...};
繼承的實現很簡單,只須要把子類的prototype設置爲父類的一個對象便可。注意這裏說的但是對象哦!
那麼經過prototype屬性實現繼承的原理是什麼呢?仍是先看圖形說明,而後編寫代碼進行驗證。
注意:紅色的方框就是把子類與父類連接起來的地方。這個就應該是傳說中的prototype鏈了吧。下面有代碼進行驗證
js代碼:
function Person(name)
{
this.name=name;
this.showMe=function()
{
alert(this.name);
}
};
Person.prototype.from=function()
{
alert('I come from prototype.');
}
var father=new Person('js');//爲了下面演示使用showMe方法,採用了js參數,實際多采用無參數
alert(father.constructor);//查看構造函數,結果是:function Person(name) {...};
function SubPer()
{
}
SubPer.prototype=father;//注意這裏
SubPer.prototype.constructor=SubPer;
var son=new SubPer();
son.showMe();//js
son.from();//I come from prototype.
alert(father.constructor);//function SubPer(){...}
alert(son.constructor);//function SubPer(){...}
alert(SubPer.prototype.constructor);//function SubPer(){...}
根據上圖的prototype鏈,還有代碼的結果,我想應該明白爲何使用prototype可以實現
JS中的繼承了吧。
到此,上面的圖應該很清楚了。再來一張手繪版的,有點不清楚哈