Javascript 中的原型鏈、prototype、__proto__的關系

sywangyc 8年前發布 | 9K 次閱讀 Prototype JavaScript開發

上圖是本寶寶用Illustrator制作的可視化信息圖,希望能幫你理清Javascript對象與__proto__、prototype和原型鏈之間的關系。如果暫時看不懂也沒關系,這篇文章讓你從0變成1。

0、感性認識JS里的“德羅斯特效應”之原型鏈

如果你打開瀏覽器的控制面板,隨便輸入一個JS內置的構造器函數,比如Array,控制臺輸出的是一個名為Array的函數體,這好像并沒有什么稀奇的,但是,當你接著輸入Array.prototype,控制面板輸出了一堆我們經常用到的Array構造器的方法,把目光轉移到最下方,有一個叫__proto__的屬性,好奇的點開,

咦~?列表列出的不是Object構造器的方法么,里邊有我們非常熟悉的hasOwnProperty還有toString等方法。我們常說的一切皆對象,一切對象皆null這么玄妙的編程界哲理和這種嵌套會不會有什么關系呢?更神奇的在下邊。

繼續深入思考,Array是構造器,那么控制臺輸出的Array.prototype的所有屬性中的constructor(翻譯:構造器)又是指向什么?點開看看,之后就像身處德羅斯特效應中一樣,__proto__和constructor,還有Array構造器中常用的方法名不斷的出現,一層套一層,一層層展開,沒有盡頭。。。

一、滿滿一堂證明題課

當你也發現了JS里這種循環往復、不斷嵌套的規律后,仍然不相信自己的眼睛,于是決定親自證明一下,哦不,是好幾下。(對于初學者只靠空想太燒腦,于是本寶寶做了幾個GIF動圖。)

1、怎么證明你就是你?

這個問題可難不倒JS中的構造器們,一個證明秒秒鐘證明“我就是我”,是顏色不一樣的煙火。

拿Array舉例,Array.prototype中有一個constructor屬性,這個屬性的值就是Array構造器自己!

2、“遺傳進化鏈__proto__”,怎么證明一切皆對象?

所有的JS內置構造器都本是對象,這要從long long ago說起。可是從什么線索開始向過去前進呢,原型鏈(我給他起了一個名字叫遺傳進化鏈)就是突破口。所有JS構造器(當然不止構造器有)都有一個__proto__屬性,這是原型鏈指針,指向進化成它的“那個”。(就像我的__proto__指針指向我父母,我父母各自的__proto__指向他們的父母... ...鬼知道最盡頭的Ta經歷了什么!?(手動黑人問號臉)。在進化的過程中,父母的遺傳物質(屬性、方法)被我的__proto__指針引用著,同時我也變異出了自己的能力(屬性、方法),然后我的子代的__proto__指針又引用我的遺傳物質。額!為毛突然莫名臉紅~)

從上圖中發現,JS內置構造器其中之一的Array原本就是一個函數,而這個函數就是Function的prototype,所以Function.prototype有的方法,JS內置構造器都有,比如call()、apply()、bind()等(其實我們自定義的函數也是繼承自Function.prototype,所以我們自己也可以定義構造器,創造屬于自己的小小王國)。(所以在編程的世界里模擬一個宇宙系統是有可能的,說不定我們本來就生活在被設定好的編程世界里哦。)

而Function.prototype的進化鏈指針又指向了Object.prototype。

3、怎么證明到頭來一切都是空?

不管你從那個屬性開始,連續引用__proto__的指針,最后輸出的那個值就是null。

上圖中,一時手殘,打成大寫的P了。

萬物都是從無生出來的?!Hi boy 老聃的道德經han莊子 讀多了吧

4、怎么證明所有JS內置構造器和自定義函數都是Function構造器的原型(prototype)。

不斷強化這一認知,實踐出真知。

①、String構造器的進化鏈指針__proto__指向Function構造器的原型

//String構造器
String.__Proto__ === Function.prototype

②、Number構造器的進化鏈指針__proto__指向Function構造器的原型

//Number構造器
Number.__Proto__ === Function.prototype

③、Boolean構造器的進化鏈指針__proto__指向Function構造器的原型

//Boolean構造器
Boolean.__Proto__ === Function.prototype

④、Array構造器的進化鏈指針__proto__指向Function構造器的原型

//Array構造器
Array.__Proto__ === Function.prototype

⑤、沒錯,Function構造器的進化鏈指針__proto__也指向自己的原型

//Function構造器
Function.__Proto__ === Function.prototype

⑥、Date構造器的進化鏈指針__proto__指向Function構造器的原型

//Date構造器
Date.__Proto__ === Function.prototype

⑦;、Error構造器的進化鏈指針__proto__指向Function構造器的原型

//Error構造器
Error.__Proto__ === Function.prototype

⑧、Object構造器的進化鏈指針__proto__指向Function構造器的原型

//Object構造器
Object.__Proto__ === Function.prototype

⑨、RegExp構造器的進化鏈指針__proto__指向Function構造器的原型

//RegExp構造器
RegExp.__Proto__ === Function.prototype

⑩、Event構造器的進化鏈指針__proto__指向Function構造器的原型

//Event構造器
Event.__Proto__ === Function.prototype

這里需要注意所有構造器的prototype都是對象(object)類型,只有Function.prototype是函數(function)類型,這是為了保證函數構造器們的__proto__指向的都是函數。

二,咦?JSON和Math哪去啦?

JS內置的構造器函數都可以使用new關鍵字實例化一個對象,我們稱實例化后的這個對象就是某某構造器的一個實例。就像我們每一個“人”都是“人類”這個構造器函數的一個實例

//實例化一個String構造函數
var str = new String("Hi, today! "); 

既然上邊10個構造器函數都能這樣實例化對象,那么JSON和Math是不是也可以用new 關鍵字實例化呢?試試看!

//嘗試實例化JSON和Math兩個構造器函數
var json = new JSON(); var Math = new Math(); 

哦No,不可以。JSON和Math不是構造器函數,他們是普通的對象。

上邊提到過,只有構造器函數才能使用new 關鍵字實例化一個對象,而JSON和Math已經是對象了,所以我們可以不用實例化直接使用JSON和Math中的屬性和方法~~(我們實例化的目就是想用實例化后的對象里的屬性和方法,那么既然JSON和Math已經是對象了,就省去實例化的操作嘍。當然,能實例化有能實例化的好處~)

所以JSON和Math不屬于10個構造器函數,但他們12個共同屬于Javascript的內置對象。

三、__proto__進化鏈指針設計為什么如此重要!!

javascript中為什么會有__proto__原型鏈的設計,不防做一個小實驗先。

//實例化一個String對象
var str = new String("Hi!")

我們先實例化一個String對象并將其賦值給str這個變量,然后我們輸出這個str

從str輸出的內容來看,str有四個屬性,分別是0、1、2、length

//我們一個個輸出這些屬性
console.log(str[0]) //H console.log(str[1]) //i console.log(str[2]) //! console.log(str['length']) //3

這毋庸置疑,但是接著往下看。

//charAt()是str對象中不存在的屬性方法,但是沒有報錯,依然可以輸出!
str.charAt(0) // 輸出'H'

charAt()是str對象中不存在的屬性方法,但是沒有報錯,依然可以輸出!

這是為什么?這就是進化鏈__proto__的用處。

str這個對象本身的確沒有charAt()這個方法,但是str的進化鏈上存在這個屬性方法,那么charAt()這個方法在進化鏈的那個節點上呢?

哦~,原來String.prototype擁有charAt這個方法,而str的__proto__指針指向String.prototype

有小伙伴就問了,“那為什么我爺爺寫得一手好毛筆字,可我卻沒遺傳這一點呢?”“呵呵”

這么說的話,str.__proto__.__proto__指向的對象所擁有的屬性str也都可以直接用嘍?答案是肯定的!

看到str.__proto__.__proto__指向的對象所擁有的屬性中有一個hasOwnProperty屬性方法了么,str可以直接使用這個屬性方法。

在驗證之前先說下str.__proto__.__proto__指向了誰?指向的是Object的prototype屬性。

str.__proto__.__proto__ === Object.prototype // true

Object.prototype.hasOwnProperty()屬性方法用來檢驗一個對象是否自己擁有一個屬性而非通過進化鏈__proto__繼承來的屬性。

好的,實驗開始:

//檢查str是否擁有length屬性
str.hasOwnProperty('length') //true //檢查str是否擁有0屬性(str.charAt(0)的輸出是'H') str.hasOwnProperty(0) //true //檢查str是否擁有1屬性 str.hasOwnProperty(1) //true //檢查str是否擁有2屬性 str.hasOwnProperty(2) //true //檢查str是否擁有3屬性(str的length是3,所以索引值從0開始,所以索引最大是2,所以沒有3這個屬性) str.hasOwnProperty(3) //false //str是否擁有hasOwnProperty這個屬性呢?答案是否定的。 str.hasOwnProperty('hasOwnProperty') //false

四、結語

現在再來看這張圖,是不是思路清晰多了呢!

當你弄清楚了原型鏈(我喜歡叫他進化鏈)__proto__,prototype之間的關系,還有Javascript中12個內置對象,其中10個函數類型,2個對象類型。再來學習這12個內置對象的屬性和屬性方法是不是如魚得水,心里跟明鏡似的~

對于Javascrip初學者,一時半會肯定還是搞不清楚,唯一的辦法就是多看、多想、多模仿、多創新、多總結、多分享~,學習的本質無非就是這些嘛,教育的本質無非就是教會你用學習的本質學習你所感興趣的嘛。

 

來自:http://www.cnblogs.com/libin-1/p/5955208.html

 

 本文由用戶 sywangyc 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!