1.每個函數(類)天生自帶一個屬性prototype,屬性值是一個對象,裡面存儲了目前類供執行個體調用的屬性和方法
2.在浏覽器預設給原型開辟的堆記憶體中有一個constructor屬性:存儲的是目前類本身(注意:自己開辟的堆記憶體中預設沒有constructor屬性,需要自己手動添加)
3.每個對象都有一個__proto__屬性,這個屬性指向目前執行個體所屬類的原型(不确定所屬類,都指向Object.prototype)
大家可以先仔細分析下該圖
首先來介紹下 <code>prototype</code> 屬性。這是一個顯式原型屬性,隻有函數才擁有該屬性。基本上所有函數都有這個屬性,但是也有一個例外
如果你以上述方法建立一個函數,那麼可以發現這個函數是不具有 <code>prototype</code> 屬性的。
當我們聲明一個函數時,這個屬性就被自動建立了。
并且這個屬性的值是一個對象(也就是原型),隻有一個屬性 <code>constructor</code>
<code>constructor</code> 對應着構造函數,也就是 <code>Foo</code>。
<code>constructor</code> 是一個公有且不可枚舉的屬性。一旦我們改變了函數的 <code>prototype</code> ,那麼新對象就沒有這個屬性了(當然可以通過原型鍊取到 <code>constructor</code>)。
那麼你肯定也有一個疑問,這個屬性到底有什麼用呢?其實這個屬性可以說是一個曆史遺留問題,在大部分情況下是沒用的,在我的了解裡,我認為他有兩個作用:
讓執行個體對象知道是什麼函數構造了它
如果想給某些類庫中的構造函數增加一些自定義的方法,就可以通過 <code>xx.constructor.method</code> 來擴充
這是每個對象都有的隐式原型屬性,指向了建立該對象的構造函數的原型。其實這個屬性指向了<code>[[prototype]]</code>,但是 <code>[[prototype]]</code> 是内部屬性,我們并不能通路到,是以使用 <code>__proto__</code> 來通路。
因為在 JS 中是沒有類的概念的,為了實作類似繼承的方式,通過 <code>__proto__</code> 将對象和原型聯系起來組成原型鍊,得以讓對象可以通路到不屬于自己的屬性。
執行個體對象的 <code>__proto__</code> 如何産生的 從上圖可知,當我們使用 new 操作符時,生成的執行個體對象擁有了 <code>__proto__</code>屬性。
是以可以說,在 new 的過程中,新對象被添加了 <code>__proto__</code> 并且連結到構造函數的原型上。
1.建立一個空對象
2.連結到原型
3.綁定this
4.傳回該對象
對于執行個體對象來說,都是通過 <code>new</code> 産生的,無論是 <code>function Foo()</code> 還是 <code>let a = { b : 1 }</code>
對于建立一個對象來說,更推薦使用字面量的方式建立對象。因為你使用 <code>new Object()</code>的方式建立對象需要通過作用域鍊一層層找到 <code>Object</code>,但是你使用字面量的方式就沒這個問題。
對于對象來說,<code>xx.__proto__.contrcutor</code> 是該對象的<code>構造函數</code>,但是在圖中我們可以發現 <code>Function.__proto__ === Function.prototype</code>,難道這代表着 <code>Function</code> 自己産生了自己?
答案肯定是否認的,要說明這個問題我們先從 Object 說起。
從圖中我們可以發現,所有對象都可以通過原型鍊最終找到 <code>Object.prototype</code> ,雖然 <code>Object.prototype</code> 也是一個對象,但是這個對象卻不是 <code>Object</code> 創造的,而是引擎自己建立了 <code>Object.prototype</code> 。是以可以這樣說,所有執行個體都是對象,但是對象不一定都是執行個體。
接下來我們來看 <code>Function.prototype</code> 這個特殊的對象,如果你在浏覽器将這個對象列印出來,會發現這個對象其實是一個函數。
我們知道函數都是通過 <code>new Function()</code> 生成的,難道 <code>Function.prototype</code> 也是通過 <code>new Function()</code> 産生的嗎?
答案也是否定的,這個函數也是引擎自己建立的。首先引擎建立了 <code>Object.prototype</code> ,然後建立了 <code>Function.prototype</code> ,并且通過 <code>__proto__</code> 将兩者聯系了起來。這裡也很好的解釋了上面的一個問題,為什麼 <code>let fun = Function.prototype.bind()</code> 沒有 <code>prototype</code> 屬性。因為 <code>Function.prototype</code> 是引擎建立出來的對象,引擎認為不需要給這個對象添加 <code>prototype</code> 屬性。
是以我們又可以得出一個結論,不是所有函數都是 <code>new Function()</code> 産生的。
有了 <code>Function.prototype</code> 以後才有了 <code>function Function()</code> ,然後其他的構造函數都是 <code>function Function()</code> 生成的。
現在可以來解釋 <code>Function.__proto__ === Function.prototype</code> 這個問題了。因為先有的 <code>Function.prototype</code> 以後才有的 <code>function Function()</code> ,是以也就不存在雞生蛋蛋生雞的悖論問題了。對于為什麼 <code>Function.__proto__</code> 會等于 <code>Function.prototype</code>
<code>Object</code> 是所有對象的爸爸,所有對象都可以通過 <code>__proto__</code> 找到它
<code>Function</code> 是所有函數的爸爸,所有函數都可以通過 <code>__proto__</code> 找到它
<code>Function.prototype</code> 和 <code>Object.prototype</code> 是兩個特殊的對象,他們由<code>引擎</code>來建立
除了以上兩個特殊對象,其他對象都是通過<code>構造器</code> <code>new</code> 出來的
函數的 <code>prototype</code> 是一個對象,也就是原型
對象的 <code>__proto__</code> 指向原型, <code>__proto__</code> 将對象和原型連接配接起來組成了<code>原型鍊</code>
作者:前端南玖
出處:https://www.cnblogs.com/songyao666/
-------------------------------------------
個性簽名:智者創造機會,強者把握機會,弱者坐等機會。做一個靈魂有趣的人!
如果覺得這篇文章對你有小小的幫助的話,可以關注下方公衆号,在該公衆号同樣會推送技術文章給大家,謝謝~
歡迎加入前端技術交流群:928029210