文章開頭說的話
首先你必須明白(或者記住)的JavaScript常識:瀏覽器
- 在JavaScript中每一個函數都有一個prototype屬性
- 在JavaScript中每一個對象都有一個__proto__屬性
- 在JavaScript中函數是一等公民,即函數也是對象
prototype和__proto__
prototype究竟是個啥呢?下面看下這段代碼,咱們慢慢來函數
// Animal是個構造函數,因此有prototype屬性
function Animal(){}
// 在prototype上定義eat方法
Animal.prototype.eat = function(food){
console.log("it is eating " + food);
}
// 構造函數實例化a1
const a1 = new Animal();
// 構造函數實例化a2
const a2 = new Animal();
// 調用實例的方法
a1.eat("food");
a2.eat("food");
從上面的代碼中,咱們能夠看到:spa
- 函數的prototype指向一個對象
- 函數實例化後的對象能夠獲取prototype指向對象的方法(和屬性)
那他們以前的關係是怎麼樣的呢?
prototype
從圖中咱們能夠看到:code
- Animal的prototype指向一個對象
- Animal的實例經過__proto__關聯到Animal的prototype指向的對象
用官方術語說,就是:對象
- 函數的prototype所指向的對象就是該函數建立的實例的原型(即:a2和a2的原型是Animal.prototype)
那麼問題來了,什麼是原型呢?
在JavaScript中,每一個對象(null除外)在建立的時候都會與之關聯另一個對象,對象和原型之間經過__proto__進行關聯blog
原型的做用
在上面的代碼中,咱們能夠看到實例對象中並無eat方法,可是每一個實例對象均可以調用eat方法,那中間的過程是怎樣的呢?圖片
- 當咱們調用實例對象(a1和a2)的方法(eat)的時候,若是找到則直接調用實例對象的方法或者屬性;若是找不到,就會查找與之關聯的原型上是否有這個方法,若是這個原型沒有,就會繼續向上查找該原型的原型(原型的原型後面探討)
原型的原型
在上面咱們提到了若是在原型上找不到相應的屬性或者方法,就會在原型的原型上查找,那麼什麼是原型的原型呢?ip
- 首先在文章開頭咱們說每一個對象都有原型,而原型也是對象,因此原型也是有原型的(聽起來有點繞)
- 那以前代碼的Animal.prototype的原型指向哪裏呢(即Animal.prototype.__proto__)指向誰呢?這裏Animal.prototype是JavaScript內置構造函數Object生成的呢,那是否是應該指向Object.prototype呢?答案是是的。
- 那Object.prototype也是對象,它的原型呢?Object.prototype.__proto__指向哪一個對象呢?答案是:null;即:
Object.prototype.__proto__ === null // true
// 表示若是查找屬性的時候到Object.prototype時仍是沒有就中止,沒有了
最後畫張圖:
原型鏈
原型鏈
注意到上圖中的藍色線條部分了嗎,這就是大名鼎鼎的原型鏈。
補充的知識
- constructor: 這個是原型中的自帶屬性,指向構造函數
- __proto__: 這個屬性實際上是瀏覽器實現的,不是標準的訪問原型的方式;ES5中規定的正式方法是:Object.getPrototypeOffang'fa
Object.getPrototypeOf(a1) === Animal.prototype // true
以上知識,最終的圖以下:

思考題:
- 在文章開頭咱們說過函數也是對象,既然是對象就有原型,那Animal的原型指向誰呢?
- Function.prototype === Function.__proto__ 是true嗎?