javascript原型等概念

研究javascript的原型概念兩天,看到這篇文章後,終於恍然大悟,裏面的圖展現了一切!大部份內容轉載自:https://segmentfault.com/a/1190000005824449javascript

一切皆爲對象

卻不知,JavaScript的世界中的對象,追根溯源來自於一個 nulljava

「一切皆爲對象」,這句着實是一手好營銷,易記,易上口,印象深入。segmentfault

萬物初生時,一個null對象,憑空而生,接着ObjectFunction學着null的模樣塑造了本身,而且它們彼此之間喜結連理,提供了prototypeconstructor,一個給子孫提供了基因,一個則製造萬千子子孫孫。瀏覽器

在JavaScript中,null也是做爲一個對象存在,基於它繼承的子子孫孫,當屬對象。乍一看,null像是上帝,而ObjectFunction猶如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.getPrototypeofObject.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關係的方法:

  1. instanceof 運算符返回一個布爾值,表示一個對象是否由某個構造函數建立。

  2. 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

一張歷史悠久的圖

這是一張描述了ObjectFunction以及一個函數實例Foo他們之間原型之間聯繫。若是理解了上面的概念,這張圖是不難讀懂。

從上圖中,能看到一個有趣的地方。

  1. Function.prototype.__proto__ 指向了 Object.prototype,這說明Function.prototype 是一個 Object實例,那麼應當是先有的Object再有Function

  2. 可是Object.prototype.constructor.__proto__ 又指向了 Function.prototype。這樣看來,沒有FunctionObject也不能建立實例。

這就產生了一種類「先有雞仍是先有蛋」的經典問題,究竟是先有的Object仍是先有的Function呢?
這麼哲學向的問題,留給你思考了。

總結一句話:

對象:
自身屬性
構造器屬性(constructor)
原型(__proto__)

函數(構造方法):
自身屬性
構造器屬性(constructor)
原型屬性(prototype)
原型(__proto__)

對象.原型(__proto__) === 函數(構造方法).原型屬性(prototype)對象.構造器屬性 === 函數(構造方法)

相關文章
相關標籤/搜索