1. 程式人生 > >__proto__ 和 prototype 深度剖析

__proto__ 和 prototype 深度剖析

為什麼要進行深度剖析

proto 和 prototype 是一個老生常談的話題,也是作為一個前端開發人員必須搞懂的問題,且不應該有任何的折扣,這是通往高階前端開發的必經之路和必備技能。雖然實際開發中用的並不多,但是當你學習一些新的js框架乃至自己封裝一些高效能的元件時是及奇有用的。
網上的相關資料很多,講解也是多種多樣,但是至今都沒有看到一篇自己滿意的文章,於是在翻閱眾多資料以及相關書籍之後,作者我一鼓作氣,擼起袖子——就是幹。

在正式進行講解時,各位看官務必弄清楚以下兩個問題。

什麼是 proto(高層三中被稱之為 [[Prototype]]) ? 什麼是prototype

  • 初學javascript我們就知道,萬物皆物件,proto__符號是用於指向someObject的原型,所有的物件都具有屬性__proto
  • prototype 是 function 特有的屬性,用來指向 function 的 原型

請允許我先上一張從百度搜索出來的圖:

看到這裡我知道你開始吐槽了,和網上看到的大部分一樣,然後表示一臉懵逼
別急,接下來我會為你一一剖析每一個過程

為了配合講解,先上一段程式碼,邊思考邊往下看(程式猿的世界裡最講道理的就是這東西了):

Function.prototype.b = 666; // Funciton 中新增屬性b
Object.prototype.a = 8888; // Object 中新增屬性a function foo() { this.d = 999; } // 建構函式建立的例項 var f1 = new foo(); // 例項 object console.log(f1.a); // 8888 console.log(f1.b); // undefined console.log(foo.a); // 8888 console.log(foo.b); // 666 var o1 = new Object(); console.log(o1.a); // 8888 console.log(o1.b); // undefined
console.log(Object.a) // 8888 console.log(Object.b) // 666
  • 首先,我們分別在 Function 和 Object 的原型中分別添加了屬性 b 和 a
  • 然後建立了一個建構函式 foo
  • 通過 new 構建了一個新的物件

建構函式繼承


接下來我們結合程式碼來看如下圖中的關係:

var f1 = new foo(); // 例項 object
console.log(f1.a); // 8888
console.log(f1.b); // undefined

通過 new 構造出來的例項 f1 的 proto 指向了建構函式【foo】的原型, foo 的 proto 直接指向了 Object.prototype ,我們剛才手動在 Object 的 prototype 上添加了屬性a。Object.prototype.a = 8888; 所以有了如下結果:

console.log(f1.a); // 8888

有因為 new 出來的 f1 僅僅是 Object 的例項,最終只能繼承 Object原型上的屬性,所以對於沒有的屬性 b 自然就是 undefined

console.log(f1.b); // undefined

函式繼承


到此我們瞭解了建構函式例項、建構函式、以及建構函式 prototype 之間的關係,接下來,我們在身下的部分,如圖所示:

console.log(foo.a); // 8888
console.log(foo.b); // 666

有圖中標號為1的線,我們知道 foo 的 proto 指向 Function.prototype 所以就有了

Function.prototype.b = 666; // Funciton 中新增屬性b
console.log(foo.b); // 666

根據圖中的4號線的指向,Function.prototype 的 proto 指向 Object.prototype 所以就有了

Function.prototype.b = 666; // Funciton 中新增屬性b
console.log(foo.a); // 8888

也就是說 foo 同時繼承了 Function.prototype 和 Object.prototype 的屬性

物件的繼承


var o1 = new Object();
console.log(o1.a); // 8888
console.log(o1.b); // undefined

o1 為 Object 的例項, Object 的原型指向 Object.prototype,所以o1 自然而然的繼承了 Object.prototype 的屬性

整個圖中最難以理解的部分就是 Object 和 Function.prototype 的關係了,為啥 物件又函式扯上關係了,也不知道當初祖師爺是怎麼想的,既然如此,路一行程式碼看看就知道了。

console.log(Object.a) // 8888
console.log(Object.b) // 666

果然不出所料,Object 同時繼承了 Object.prototype 和 Function.prototype 的屬性

一開始,我們的程式碼就用程式碼展示了 這個經典的圖中的 proto 和 prototype 之間的關係,而後有簡單的結合程式碼做了展示,相信各位也已經看懂了,也瞭解 函式的例項、建構函式的例項、建構函式、函式以及物件之間的關係。在這裡作者我需要向各位強調以下必須注意的知識點

  • 所有的 Object 都有 proto 屬性
  • prototype 是 function 所特有的
  • 通過 new 構造出來的object,無論是 new foo() 還是 new Object() 所構造出來的 object,最終都會繼承 Object.prototype
  • new foo() 的例項既可以繼承 foo.prototype 也可以繼承 Object.prototype
  • new Object() 只能繼承 Object.prototype
  • 只有 Object 才可以繼承 Function.prototype

記住以下幾點,我想足夠縱橫 proto 和 prototype 的關係,徹徹底底的弄明白原型鏈之間的工種關係。測地弄明白這張圖和這幾行程式碼個人認為足夠了,如果各位看官還是覺得有點吃力,好好看看 《高階程式設計第三版》PDF請自行非同步這裡下載 的第六章關於原型鏈的講解,看完之後在回過頭來看看這圖,相信一定會荒原大悟。

小結


更多的乾貨請點選這裡
react-native 實戰專案學習
歡迎各位看官的批評和指正,共同學習和成長
希望該文章對您有幫助,您的鼓勵和支援是我持續的最大動力