原形還是原型?

原形還是原型?

本系列文需要一定js基礎,適合讀者:了解原型與原型鏈,但不清晰。

先拋出一個結論,

__proto__ 是原形,prototype 是原型,原形和原型不同,原形是一個指針,而原型是一個實體

如果你能讀懂這句話,那麼這篇文章你不用看了。

大家應該都玩過王者榮耀,今天我們藉由一個創造王者榮耀英雄的過程來講解一下原型與原型鏈。首先我們先定義一個創造英雄的方法:

varHero=function(name,props){this.name=name// 名字
this.props=props// 属性
}

挺簡單的,不用多解釋了。

接下來創造一個英雄,名字叫阿珂吧。

varake=newHero('ake',{hp:100,mp:0,})

再創造幾個英雄吧,劉備,典韋之類的,也是類似的。

vardianWei=newHero('dianWei',{hp:500,mp:20,})varliuBei=newHero('liuBei',{hp:1000,mp:100,})

實際上,早期的JavaScript就是用這種構造函數方式來模擬“類”的行為。

接下來,讓我們來看一下prototype 和__proto__ 吧。

prototype

此時,所有的英雄就構造完成了,但是,所有英雄都有回城技能,那麼該如何添加呢?總不會一個一個英雄去修改吧?我們觀察,以上英雄均有相同的特點,即一個名字加一個屬性,那麼在創造這些英雄的過程中是否存在著一個參考呢?或者說一個模板?是的,這就是prototype ,也就是真正的原型,我更願意將其稱之為母體。

所有由母體克隆而出的複制體,都是基於母體的,因此也會擁有母體所具有的特徵。例如,男程序員和男產品經理都是來自於男性,因此,假如有一天國家規定所有男性必須穿裙子,那麼男程序員和男產品經理必不可免的要穿上裙子。

那麼,問題其實就很簡單地解決了,只需要讓英雄們的母體具有回城功能就可以了。

Hero.prototype.goHome=function(){...// 回城
}

__proto__

新手們看到這段代碼通常會很迷惑,通常會發出如下的疑問:

等等!我學過這裡,prototype不是原型的意思嗎?你這樣不是修改了Hero的原型嗎?也不就是修改了Hero的母體嗎? ake來自於Hero,你應該這麼寫Hero.goHome = function() { ... }才對!

這就是今天的重點, 原型和原形之爭。實際上prototype和__proto__經常容易搞混,這是因為雖然prototype翻譯為原型,但大家經常會把它錯誤地按照原形的意思理解。

看過EVA的朋友們一定會知道零號機和二號機,EVA中,政府基於零號機創造出了一系列的機器人,分別取名為二號機,三號機,N號機,實際上歸納為一句話就是

二號機的原形零號機

這句話中,原形就是__proto__ ,而零號機就是prototype,用代碼來表示就是

'二号机'.__proto__==='零号机'// 二号机的 原形 是零号机
'机器人'.prototype==='零号机'// 机器人的 原型 是零号机

再來說說,新手的寫法為什麼錯誤。 Hero實質上是一個類似於構造器一樣的東西,一個加工工廠,對工廠做任何改動不會對產品產生影響,如果想要改動所有的產品,當然要對模具進行改動。

這麼說來,就一切都說通了,因此我們可以得出——

一系列推論

  • 實例沒有創造其他東西的能力,那麼它一定沒有原型
  • 然而構造函數來自於Function,那麼它一定有原形(玩具工廠也是一種工廠)

那麼-構造函數的原形就應該是Function的原型

讓我們來驗證一下,

ake . prototype // undefined Hero . __proto__ === Function . prototype // true

結果符合的非常好。

Function

我們知道,所有的函數都來自於Function,那Function又來自哪裡呢?答案是:來自V8引擎內部。

為了讓Function也符合函數的特徵,js強行規定了Function.__proto__ === Function.prototype。

Object

我們又知道,對象最終都來自於Object,也就是說沿著__proto__追溯,最終總會到達Object.prototype, 然而問題來了,Object.prototype的原形是誰呢?答案是:null,不存在

是的,道生一,一生萬物。

一些有趣的結論

基於以上結論,我們會得到一些很好玩的事情,例如:

Object.__proto .__proto指向什麼呢?

讓我們來分析一下,Object實質上也是一個構造函數,那麼它應該也來自Function,沒錯,Object.__proto__指向的就是Function.prototype。

問題轉化為

Function.prototype.__proto__指向什麼呢?

看了上面的分析之後,Function.prototype顯然是一個對象,那麼問題就是一個對象的原型是什麼。答案很顯然,

Object.prototype

讓我們來驗證一下,

Object.__proto__.__protp__===Object.prototype// true

原型鏈

原型鏈也很簡單,基本呼之欲出了。舉個例子,如果你想知道一個男前端是否會跑步,雖然答案顯然可見,不過你也可以這樣想:

男前端是一個男人, 男人是人類, 人類都會跑步。

這就是原型鏈。當你不確定一個對像是否擁有一種屬性或者方法的時候,你可以向上去尋找它的原形(__proto__)。直到找到Object為止。

那麼原型鏈傳下來的方法與屬性,為什麼不在對像中直接表示出來呢?再次回到王者榮耀,現實中,我們在描述一個英雄的時候,一般也都會說它的獨特技能,而不會說它會回城,對不對?

後續還有Constructor等內容,未完待續

What do you think?

Written by marketer

從一個簡單的實例看JavaScript 的異步編程進化歷

淺談ES 模塊和Webpack Tree-shaking