如何講清楚JS原型鏈?
這篇文章假定你熟悉基本的建構函式及json物件的用法
這篇文章假定你能夠分清楚函式的this指向問題
如果你暫時還搞不定,請先閱讀文章
這篇文章同時假定你知道什麼是資料結構,以及單向連結串列
先看一個典型的單向連結串列
上面這段程式碼,是一個典型的
單向連結串列
我們只能透過
n.next
來找到下一個節點
但是沒有辦法從當前節點找到上一個節點
不過這和原型鏈有什麼關係呢?
目前還沒有關係。
原型物件從哪來?
分析上面這段程式碼
為什麼
obj
明明是空的,卻還能呼叫
toString
方法?
這
toString
方法是哪裡來的呢?
再比如我們的陣列物件
其實,這些方法都來自於一個叫做原型的傢伙
我們可以使用 物件
.__proto__
的形式把原型物件打印出來
這是所有物件的一個隱式屬性, 也就是正常情況下列印物件, 我們是看不到這個屬性的
但我們依然可以透過
__proto__
這樣一個比較奇怪的屬性名字來訪問原型物件
這個屬性名稱訪問起來確實不夠方便,實際上它還有另外一種訪問方式
也是一種比較正式的訪問方式, 就是透過函式名來訪問
例如看下面這個例子
現在我們大概有了一些疑問
1。原型物件中的所有屬性,例項物件都能隨便訪問嗎?
2。每個例項物件都有自己的原型物件, 還是大家共用一個?
3。例項物件自己的屬性和原型的屬性衝突了, 會訪問誰呢?
4。原型物件跟繼承有什麼關係?
5。原型鏈又是怎麼來的?
我們依次來解決這幾個疑問
原型物件中的所有屬性,例項物件都能隨便訪問嗎?
廢話不多說, 我們來試驗一下, 就知道結果
在上面的程式碼中,我們建立一個建構函式
Phone
同時我們給原型物件,添加了
price、color
兩個屬性
同時添加了
playmusic、phonecall
兩個方法
接下來我們透過例項物件來訪問一下這些內容
可以看到,原型中的屬性和方法都可以被例項物件直接訪問!
每個例項物件都有自己的原型物件, 還是大家共用一個?
這是一道非常簡單的數學證明題,證明過程如下:
最終結論是, 所有例項物件共享同一個原型物件
例項物件自己的屬性和原型的屬性衝突了, 會訪問誰呢?
我們再來試驗一下
可以看到, 如果物件本身存在這個屬性或方法, 會優先訪問自己的
如果沒有, 則訪問原型的屬性, 訪問過程如下圖
原型物件跟繼承有什麼關係呢 ?
請你再思考一個問題
如果原型也是一個物件
那麼它必然也應該有自己的原型物件,不是嗎?
我們可以透過
p1.__proto__.__proto__
進行訪問
我們把圖畫的再簡單一點
從圖中可以看出,只要原型物件一直存在
物件P1就擁有了所有原型物件的能力, 我們也管這個叫做
繼承
而這些原型物件之間是什麼關係呢?
p1
p1
。
__proto__
p1
。
__proto__
。
__proto__
p1
。
__proto__
。
__proto__
。
__proto__
原型鏈, 因此而得名
需要補充的問題
原型物件真的沒有盡頭嗎?
當然不是的, 原型物件由瀏覽器自動建立, 當然也有它自己的規則
規則如下:
1.每個建構函式在誕生的時候, 都會建立一個該函式的例項物件作為預設原型
相當於
Phone。prototype = new Phone();
2.而這個原型物件的原型, 則預設指向Object.prototype
相當於
Phone。prototype。__proto__ = Object。prototype;
3.當然, Object.prototype 也是它自己的例項
相當於
Object。prototype = new Object();
4. 但是, Object.prototype不再擁有原型物件
相當於
Object。prototype。__proto__ = null
5. 因此,原型物件是有上限的
p1。__proto__
可訪問
p1。__proto__。__proto__
可訪問
p1。__proto__。__proto__。__proto__
為null
原型的最重要的作用就是擴充套件能力