以前說到原型鏈就只知道通過原型鏈繼承,構造函數和它的實例之間的原型鏈的關係啊這些東西,最近面試被問得很懵,後來看到一篇文章總結的特別好,當時看明白了,後來面試又忘了一部分,所以今天打算自己總結一下,加深印象。
JS中沒有類的概念,萬物皆是對象
其他語言中的繼承於類,在JS中是 對象繼承於對象。所以萬物都有__proto__
屬性,但是prototype
屬性卻不是所有對象都有,只有構造函數纔有。
例如 使用instanceof
用來測試變量是不是Object類型,除了基本數據類型外,其他的比如函數、數組、鍵值對對象以及比較特殊的幾個構造函數——Number、Array、String、Boolean、Function等,用instanceof Object
結果都是true
。
instanceof
的原理是通過原型鏈一直查找,找到相同的原型就會返回true
。- 在我們平時使用構造函數實例化對象的過程中,我們知道對象有一個屬性叫
__proto__
,這個屬性是指向它的構造函數的原型prototype
。
面試中被問到typeof Object
返回什麼?
這個回答上來了,是function
,因爲Object是對象的構造函數,它是Function的實例,實際上所有的構造函數例如Array、Number都是Function的實例。下面我們通過代碼使用上面兩條測試來證明:
Object instanceof Function // true
Array instanceof Function //true
Object.__proto__ === Function.prototype // true
Number.__proto__ === Function.prototype // true
所以說Number、Array、String、Boolean
這些構造函數都是Function
的實例,那麼Function
也是構造函數,那麼Function
是不是 Function 自身的實例呢?
答案是:是的。
Function instanceof Function // true
Function.__proto__ === Function.prototype // true
如上所示,這就是神奇的地方所在了,Function
的對象屬性__proto__
指向了它自己的原型prototype
。
到這裏我們可以得出幾個結論:
- 任何對象都有
__proto__
屬性,但是隻有函數纔有prototype
屬性 - 同一個對象的
__proto__
屬性和prototype
屬性是沒有關聯的,除了Function
- 對象的
__proto__
屬性是用來和其父“類”關聯的,構造函數的prototype
是用來和其實例關聯的,所以對於同時存在這兩個屬性的構造函數來說,這兩個屬性之間是沒有什麼關係的,也就對應上一條。 - 任何構造函數都是
Function
的實例,任何對象都是Object
的實例 - 任何對象直接調用方法,例如
obj.foo()
,在實例本身沒有這個屬性的情況下,都是從其__proto__
屬性上查找的,也就是從該對象的構造函數的prototype
屬性上查找。
這裏有一道典型的題目就是考對上面幾條的理解,我們都知道Object.prototype.toString
方法可以用來判斷數據類型,那麼用Object.toString
可以嗎。
答案是:不可以。
因爲Object.toString
是Object.__proto__.toString
,也就是Function.prototype.toString
,和Object.prototype.toString
根本不是同一個函數。
下面我們通過一張圖來描述JS中的原型鏈。
JS中的原型鏈
OK,這就是JS中的原型鏈的樣子,都是圍繞前面提到的幾點進行繪製的。
參考鏈接
附上之前看的文章的鏈接:再談javascriptjs原型與原型鏈及繼承相關問題