注:本文首發於個人博客,移步 獲取更好的閱讀體驗。git
拋開封面圖,咱們先以 MDN 的一句話做爲開端,github
對於使用過基於類的語言 (如 Java 或 C++) 的開發人員來講,JavaScript 有點使人困惑,由於它是動態的,而且自己不提供一個 class
實現。雖然在 ES2015/ES6 中引入了 class
關鍵字,但那只是語法糖,JavaScript 仍然是基於原型的。瀏覽器
那麼到底什麼是原型?less
咱們先用構造函數建立一個實例對象,函數
其實理解原型,就是理解構造函數,實例對象和原型對象之間的關係,ui
function Engineer(name) {
this.name = name
}
Engineer.prototype.coding = function() {
console.log('write less, do more.')
}
const engineer = new Engineer('campcc')
複製代碼
JavaScript 中,每個構造函數都有一個 prototype
屬性,它指向構造函數的原型對象:this
Engineer.prototype // {coding: ƒ, constructor: ƒ}
複製代碼
原型對象中有一個 constructor
屬性指回構造函數:spa
Engineer.prototype.constructor === Engineer // true
複製代碼
而每個實例對象都有一個 __proto__
屬性,當咱們使用構造函數建立實例時,實例的 __proto__
屬性就會指向構造函數的原型對象:prototype
engineer.__proto__ === Engineer.prototype // true,__proto__ 實際上是一個 JavaScript 的非標準但許多瀏覽器都實現的屬性,從 ECMAScript 6 開始,支持經過符號 [[Prototype]] 或者方法 Object.getPrototypeOf() 訪問
複製代碼
構造函數,實例對象與原型對象的關係爲:3d
爲了更好的理解什麼是原型鏈,咱們嘗試調用實例對象的幾個方法,
engineer.coding() // write less, do more.
engineer.toString() // "[object Object]"
engineer.map() // Uncaught TypeError: engineer.map is not a function
複製代碼
結果看似很出乎意料,由於咱們其實並無在實例裏定義 coding
和 toString
方法啊,可是它們卻可以被成功調用,爲何?由於在 JavaScript 中,當咱們試圖訪問一個對象的屬性或方法時,它不單單在該對象上搜尋,還會搜尋該對象的原型,以及該對象的原型的原型,依次層層向上搜索:
咱們嘗試訪問 map
方法時報錯了,由於原型鏈上找不到此方法。原型鏈查找會一直持續,直到找到一個名字匹配的屬性或方法,或者達到原型鏈的末尾,而根據定義,null
就是原型鏈的末尾:
Object.prototype.__proto__ === null // true,null 在這裏能夠理解爲 「沒有對象」
複製代碼
若是查找過程當中遇到同名屬性或方法,位於原型鏈底端的屬性或方法會被優先應用,這叫作 「屬性遮蔽(property shadowing)」。好比咱們在 Engineer
的原型對象上聲明一個同名的 name
屬性:
Engineer.prototype.name = 'engineer'
engineer.name // campcc,這裏不會打印 'engineer',由於在原型鏈查找的過程當中,實例對象中就已經存在 name 屬性了
複製代碼
總結一下,原型鏈其實就是對象或原型對象的 __proto__
組成的一條原型查找鏈。
幾乎全部 JavaScript 中的對象都是位於原型鏈頂端的 Object 的實例,但有兩個例外:
關於封面圖,其實隱喻了 JavaScript 中一直存在爭議的一個問題,
Function.__proto__ === Function.prototype // true
複製代碼
先來看看在 Chorme V8 中的打印結果:
Function.__proto__ // ƒ () { [native code] }
Function.prototype // ƒ () { [native code] }
Object.__proto__ // ƒ () { [native code] }
複製代碼
從打印結果來看,根本不存在 "Function 也是 Function 自己的一個實例" 的說法,由於無論是 Function.__proto__
, Function.prototype
仍是 Object.__proto__
都是引擎建立的,Function
也是由引擎建立的,至於爲何會相等,只能說每一個語言都存在缺陷,這個問題就像爲何 typeof null === "object"
同樣,不用過於糾結。
JavaScript 中,
prototype
屬性,表明構造函數的原型對象__proto__
屬性,指向構造函數的原型對象constructor
屬性,指向構造函數,標識原型對象的是由哪一個函數構造的__proto__
組成了一條原型鏈,原型鏈其實也是一條查找鏈null
若是有疑問或者發現錯誤,能夠在相應的 issues 進行提問或勘誤
若是喜歡或者有所啓發,歡迎 star,對做者也是一種鼓勵
(完)