關於javascript中的原型和原型鏈

關於javascript中的原型和原型鏈

我GitHub上的菜鳥倉庫地址: 點擊跳轉查看其餘相關文章
文章在個人博客上的地址: 點擊跳轉

        關於javascript中的原型和原型鏈,可能都會想到一個詞「prototype」,而實際裏面藏的是什麼東西,纔是你們應該要掌握的。javascript

        看過一些文章,將原型和原型鏈說得很枯燥難懂,其實抽絲剝繭以後,理順思路,其實原型和原型鏈沒有想象中的那麼難以理解。我一直崇尚的是類比生活去理解,因此我的仍是不太喜歡純敘述性的解釋。java

        其實不少講解的人,都是從自身角度出發的,解釋的都是理所固然的,他們沒法感覺咱們這些菜鳥的角度,不知道咱們不少個爲何。固然,當咱們瞭解理解以後,再從新看他們的文章,說的也是頭頭是道的。git

        關於原型這個詞,其實很好理解,能夠說成是「原來的模型」。好比說,「兒子長得就像是爸爸一個模子出來同樣」,那爸爸就是兒子的原型,兒子繼承了爸爸的一些特徵,固然,兒子也會有本身的特徵,這些特徵,就是屬性。而有時候兒子有些特徵沒有,能夠在兒子的爸爸那裏找到,甚至兒子爸爸那裏找不到的特徵,能夠在爸爸的爸爸那裏找到,而彼此之間維繫着的,就是血緣關係,DNA傳遞,而這個關係鏈,就是咱們說的原型鏈,固然,往上找祖先,找到最後確定是炎帝黃帝了,他們就是人類始祖了,若是他們身上還找不到,再往上找,就是空了,由於往上就沒有祖先了,原本無一物,何處惹塵埃。github

        好了,開始來代碼了。網絡

        先來一個構造函數:函數

//構造一我的類
function Mankind(name){
    this.name = name;
}

//實例化一個Dad對象
var Dad = new Mankind('BaBa');

//看看Dad的名字是什麼
console.log(Dad.name);

//打印結果
BaBa

        先說一個前提:this

        只要是函數,就會有一個 prototype 屬性,能夠理解爲子代的原型(遺傳基因);只要是對象,就會有一個__proto__方法,能夠理解爲向上尋找原型的方法。spa

        因此上面的構造函數中,Mankind這個構造函數,就會有一個prototype屬性(不是函數沒有),能夠這樣訪問:Mankind.prototype,固然也能夠給傳統基因添加其餘特徵:prototype

//仍是上面的構造函數
function Mankind(name){
    this.name = name;
}

//仍是實例化一個Dad對象
var Dad = new Mankind('BaBa');

//而後給構造函數添加特徵
Mankind.prototype.sayHello = 'HaHaHa';

//看看Dad有沒有sayHello特徵
console.log(Dad.sayHello);

//打印結果
HaHaHa

        從結果能夠看出,Dad原本沒有的sayHello特徵,你給Dad的祖先添加了,Dad也會擁有這個特徵了,其實這就是從原型鏈上找到這個屬性了。code

        Dad對象這個實例的原型,就是Mankind.prototype這個遺傳基因。

        而向上找原型,就是經過__proto__這個方法,因此:

Dad.__proto__ === Mankind.prototype  //true

        固然,Mankind.prototype也是一個對象,固然也有一個__proto__方法,經過這個方法,也是能夠找到他再上一級的原型,因此:

Mankind.prototype.__proto__ === Object.prototype //true

        這也是對的。由於函數的祖先是Object,因此就是指向Object.prototype這個原型 。

固然,再往上找,就是空了。
Object.prototype.__proto__  === null  //true

        因此各個原型組織起來,就是一條原型鏈了:

        Dad ---> Mankind.prototype ---> Object.prototype ---> null   能夠看到從對象開始的原型鏈的規律

        回過頭來,其實Mankind.prototype這個對象除了__proto__這個方法外,還有一個constructor的方法,由於Mankind是函數,因此有這個方法,因此經過這個方法,能夠訪問到自身這個函數:

//打印一下Mankind.prototype.constructor
console.log(Mankind.prototype.constructor);

//打印結果
function Mankind(name){
    this.name = name;
}

        說到這裏,相信已經類比得很清楚了。而後又會有一個疑問:

        既然說函數是對象(函數對象Function,普通對象Object,Function是繼承於Object的),那麼前面的構造函數Mankind能夠有prototype屬性,也應該有__proto__這個方法?

        沒錯,因此咱們也能夠有Mankind.__proto__這個方法訪問原型:

Mankind.__proto__ === Function.prototype  //true

        固然,Function.prototype 也是能夠經過__proto__方法訪問原型:

Function.prototype.__proto__ === Object.prototype //true

        因此也有這樣的原型鏈:

        Mankind ---> Function.prototype ---> Object.prototype ---> null   能夠看到從函數開始的原型鏈的規律

        固然了,咱們既然有一個實例的對象Dad,固然也能夠再延生下去,生一個Son來繼承Dad的啦:

//從Dad那裏繼承,建立一個son對象,下面兩種方法均可以:
var Son = new Object(Dad);
var Son = Object.create(Dad);

//修改一下兒子的name
Son.name = 'ErZi';

//打印一下兒子的name和原型鏈上父親的name
console.log(Son.name);
console.log(Son.__proto__.name);//經過__proto__方法找到父親Dad

//打印結果
ErZi
BaBa

        因此這條原型鏈是這樣的:

        Son ---> Dad ---> Mankind.prototype ---> Object.prototype ---> null   對照從對象開始的原型鏈的規律

        經過上面的一大頓囉嗦,相信已經很清楚了,最後再說一下雞和雞蛋的問題:

        上面既然說到有Object.prototype,並且prototype是函數纔有的,因此能夠訪問到Object這個構造函數,能夠用Object.prototype.constructor這個方法,固然構造函數是繼承於函數對象的,因此構造函數原型又是Function.prototype,因此也有這樣的一條原型鏈:

        Object ---> Function.prototype ---> Object.prototype ---> null   對照從函數開始的原型鏈的規律(這裏的Object是構造函數)

        或者表示爲:

        Object.prototype.constructor---> Function.prototype ---> Object.prototype ---> null

        這就是雞和雞蛋的問題了。

        最最後,放上一張網絡上解釋很清楚的原型鏈圖,再結合我上面的囉嗦,相信就很清楚容易明白了。
        952839-20160807171533231-172025675.png

相關文章
相關標籤/搜索