本文同步分佈在個人 Github 上git
本文不會過多介紹基礎知識,而是把重點放在原型的各個難點上。github
你們能夠先仔細分析下該圖,而後讓咱們進入主題瀏覽器
prototype
首先來介紹下 prototype
屬性。這是一個顯式原型屬性,只有函數才擁有該屬性。基本上全部函數都有這個屬性,可是也有一個例外markdown
let fun = Function.prototype.bind() 複製代碼
若是你以上述方法建立一個函數,那麼能夠發現這個函數是不具備 prototype
屬性的。app
當咱們聲明一個函數時,這個屬性就被自動建立了。函數
function Foo() {} 複製代碼
而且這個屬性的值是一個對象(也就是原型),只有一個屬性 constructor
oop
constructor
對應着構造函數,也就是 Foo
。this
constructor
是一個公有且不可枚舉的屬性。一旦咱們改變了函數的 prototype
,那麼新對象就沒有這個屬性了(固然能夠經過原型鏈取到 constructor
)。spa
那麼你確定也有一個疑問,這個屬性到底有什麼用呢?其實這個屬性能夠說是一個歷史遺留問題,在大部分狀況下是沒用的,在個人理解裏,我認爲他有兩個做用:prototype
xx.constructor.method
來擴展_proto_
這是每一個對象都有的隱式原型屬性,指向了建立該對象的構造函數的原型。其實這個屬性指向了 [[prototype]],可是 [[prototype]] 是內部屬性,咱們並不能訪問到,因此使用 _proto_
來訪問。
由於在 JS 中是沒有類的概念的,爲了實現相似繼承的方式,經過 _proto_
將對象和原型聯繫起來組成原型鏈,得以讓對象能夠訪問到不屬於本身的屬性。
_proto_
如何產生的從上圖可知,當咱們使用 new
操做符時,生成的實例對象擁有了 _proto_
屬性。
function Foo() {} // 這個函數是 Function 的實例對象 // function 就是一個語法糖 // 內部調用了 new Function(...) 複製代碼
因此能夠說,在 new
的過程當中,新對象被添加了 _proto_
而且連接到構造函數的原型上。
在調用 new
的過程當中會發生以上四件事情,咱們也能夠試着來本身實現一個 new
function create() { // 建立一個空的對象 let obj = new Object() // 得到構造函數 let Con = [].shift.call(arguments) // 連接到原型 obj.__proto__ = Con.prototype // 綁定 this,執行構造函數 let result = Con.apply(obj, arguments) // 確保 new 出來的是個對象 return typeof result === 'object' ? result : obj } 複製代碼
對於實例對象來講,都是經過 new
產生的,不管是 function Foo()
仍是 let a = { b : 1 }
。
對於建立一個對象來講,更推薦使用字面量的方式建立對象。由於你使用 new Object()
的方式建立對象須要經過做用域鏈一層層找到 Object
,可是你使用字面量的方式就沒這個問題。
function Foo() {} // function 就是個語法糖 // 內部等同於 new Function() let a = { b: 1 } // 這個字面量內部也是使用了 new Object() 複製代碼
對於對象來講,xx.__proto__.contrcutor
是該對象的構造函數,可是在圖中咱們能夠發現 Function.__proto__ === Function.prototype
,難道這表明着 Function
本身產生了本身?
答案確定是否定的,要說明這個問題咱們先從 Object
提及。
從圖中咱們能夠發現,全部對象均可以經過原型鏈最終找到 Object.prototype
,雖然 Object.prototype
也是一個對象,可是這個對象卻不是 Object
創造的,而是引擎本身建立了 Object.prototype
。因此能夠這樣說,全部實例都是對象,可是對象不必定都是實例。
接下來咱們來看 Function.prototype
這個特殊的對象,若是你在瀏覽器將這個對象打印出來,會發現這個對象實際上是一個函數。
咱們知道函數都是經過 new Function()
生成的,難道 Function.prototype
也是經過 new Function()
產生的嗎?答案也是否認的,這個函數也是引擎本身建立的。首先引擎建立了 Object.prototype
,而後建立了 Function.prototype
,而且經過 __proto__
將二者聯繫了起來。這裏也很好的解釋了上面的一個問題,爲何 let fun = Function.prototype.bind()
沒有 prototype
屬性。由於 Function.prototype
是引擎建立出來的對象,引擎認爲不須要給這個對象添加 prototype
屬性。
因此咱們又能夠得出一個結論,不是全部函數都是 new Function()
產生的。
有了 Function.prototype
之後纔有了 function Function()
,而後其餘的構造函數都是 function Function()
生成的。
如今能夠來解釋 Function.__proto__ === Function.prototype
這個問題了。由於先有的 Function.prototype
之後纔有的 function Function()
,因此也就不存在雞生蛋蛋生雞的悖論問題了。對於爲何 Function.__proto__
會等於 Function.prototype
,我的的理解是:其餘全部的構造函數均可以經過原型鏈找到 Function.prototype
,而且 function Function()
本質也是一個函數,爲了避免產生混亂就將 function Function()
的 __proto__
聯繫到了 Function.prototype
上。
Object
是全部對象的爸爸,全部對象均可以經過 __proto__
找到它Function
是全部函數的爸爸,全部函數均可以經過 __proto__
找到它Function.prototype
和 Object.prototype
是兩個特殊的對象,他們由引擎來建立new
出來的prototype
是一個對象,也就是原型__proto__
指向原型, __proto__
將對象和原型鏈接起來組成了原型鏈