研究javascript的原型概念兩天,看到這篇文章後,終於恍然大悟,裏面的圖展現了一切!大部份內容轉載自:https://segmentfault.com/a/1190000005824449javascript
卻不知,JavaScript的世界中的對象,追根溯源來自於一個 nulljava
「一切皆爲對象」,這句着實是一手好營銷,易記,易上口,印象深入。segmentfault
萬物初生時,一個null
對象,憑空而生,接着Object
、Function
學着null
的模樣塑造了本身,而且它們彼此之間喜結連理,提供了prototype
和constructor
,一個給子孫提供了基因,一個則製造萬千子子孫孫。瀏覽器
在JavaScript中,null
也是做爲一個對象存在,基於它繼承的子子孫孫,當屬對象。乍一看,null
像是上帝,而Object
和Function
猶如JavaScript世界中的亞當與夏娃。函數
__proto__
在JavaScript中,每一個對象都擁有一個原型對象,而指向該原型對象的內部指針則是__proto__
,經過它能夠從中繼承原型對象的屬性,原型是JavaScript中的基因連接,有了這個,才能知道這個對象的祖祖輩輩。從對象中的__proto__
能夠訪問到他所繼承的原型對象。ui
var a = new Array(); a.__proto__ === Array.prototype // true
上面代碼中,建立了一個Array的實例a
,該實例的原型指向了Array.prototype
。Array.prototype
自己也是一個對象,也有繼承的原型:spa
a.__proto__.__proto__ === Object.prototype // true // 等同於 Array.prototype.__proto__ === Object.prototype
這就說了明瞭,Array自己也是繼承自Object的,那麼Object的原型指向的是誰呢?prototype
a.__proto__.__proto__.__proto__ === null // true // 等同於 Object.prototype.__proto__ === null
因此說,JavaScript中的對象,追根溯源都是來自一個null對象。佛曰:萬物皆空,善哉善哉。指針
除了使用.__proto__
方式訪問對象的原型,還能夠經過Object.getPrototypeOf
方法來獲取對象的原型,以及經過Object.setPrototypeOf
方法來重寫對象的原型。code
值得注意的是,按照語言標準,__proto__
屬性只有瀏覽器才須要部署,其餘環境能夠沒有這個屬性,並且先後的兩根下劃線,表示它本質是一個內部屬性,不該該對使用者暴露。所以,應該儘可能少用這個屬性,而是用 Object.getPrototypeof
和Object.setPrototypeOf
,進行原型對象的讀寫操做。這裏用__proto__
屬性來描述對象中的原型,是由於這樣來得更加形象,且容易理解。
prototype
函數做爲JavaScript中的一等公民,它既是函數又是對象,函數的原型指向的是Function.prototype
var Foo = function() {} Foo.__proto__ === Function.prototype // true
函數實例除了擁有__proto__
屬性以外,還擁有prototype
屬性。經過該函數構造的新的實例對象,其原型指針__proto__
會指向該函數的prototype
屬性。
var a = new Foo(); a.__proto__ === Foo.prototype; // true
而函數的prototype
屬性,自己是一個由Object
構造的實例對象。
Foo.prototype.__proto__ === Object.prototype; // true
prototype
屬性很特殊,它還有一個隱式的constructor
,指向了構造函數自己。
Foo.prototype.constructor === Foo; // true a.constructor === Foo; // true a.constructor === Foo.prototype.constructor; // true
概念:
原型鏈做爲實現繼承的主要方法,其基本思想是利用原型讓一個引用類型繼承另外一個引用類型的屬性和方法。
每一個構造函數都有一個原型對象(prototype
),原型對象都包含一個指向構造函數的指針(constructor
),而實例都包含一個指向原型對象的內部指針(__proto__
)。
那麼,假如咱們讓原型對象等於另外一個類型的實例,此時的原型對象將包含一個指向另外一個原型的指針,相應地,另外一個原型中也包含着一個指向另外一個構造函數的指針。假如另外一個原型又是另外一個類型的實例,那麼上述關係依然成立。如此層層遞進,就構造了實例與原型的鏈條,這就是原型鏈的基本概念。
意義:「原型鏈」的做用在於,當讀取對象的某個屬性時,JavaScript引擎先尋找對象自己的屬性,若是找不到,就到它的原型去找,若是仍是找不到,就到原型的原型去找。以此類推,若是直到最頂層的Object.prototype仍是找不到,則返回undefine。
在JavaScript中,也存在鑑定親子之間DNA關係的方法:
instanceof 運算符返回一個布爾值,表示一個對象是否由某個構造函數建立。
Object.isPrototypeOf() 只要某個對象處在原型鏈上,isProtypeOf都返回true
var Bar = function() {} var b = new Bar(); b instanceof Bar // true Bar.prototype.isPrototypeOf(b) // true Object.prototype.isPrototypeOf(Bar) // true
要注意,實例b
的原型是Bar.prototype
而不是Bar
這是一張描述了Object
、Function
以及一個函數實例Foo
他們之間原型之間聯繫。若是理解了上面的概念,這張圖是不難讀懂。
從上圖中,能看到一個有趣的地方。
Function.prototype.__proto__
指向了 Object.prototype
,這說明Function.prototype
是一個 Object
實例,那麼應當是先有的Object
再有Function
。
可是Object.prototype.constructor.__proto__
又指向了 Function.prototype
。這樣看來,沒有Function
,Object
也不能建立實例。
這就產生了一種類「先有雞仍是先有蛋」的經典問題,究竟是先有的Object
仍是先有的Function
呢?
這麼哲學向的問題,留給你思考了。
總結一句話:
對象:
自身屬性
構造器屬性(constructor)
原型(__proto__)
函數(構造方法):
自身屬性
構造器屬性(constructor)
原型屬性(prototype)
原型(__proto__)
對象.原型(__proto__) === 函數(構造方法).原型屬性(prototype)對象.構造器屬性 === 函數(構造方法)