為何用“繼承”為標題,而不用“原型鏈”?
原型鏈如果解釋清楚了很容易理解,不會與常用的java/C#產生混淆。而“繼承”確實常用面嚮物件語言中最基本的概念,但是java中的繼承與javascript中的繼承又完全是兩回事兒。因此,這裡把“繼承”著重拿出來,就為了體現這個不同。
javascript中的繼承是通過原型鏈來體現的。先看幾句程式碼
以上程式碼中,f1是Foo函式new出來的物件,f1.a是f1物件的基本屬性,f1.b是怎麼來的呢?——從Foo.prototype得來,因為f1.__proto__指向的是Foo.prototype
訪問一個物件的屬性時,先在基本屬性中查詢,如果沒有,再沿著__proto__這條鏈向上找,這就是原型鏈。
看圖說話:
上圖中,訪問f1.b時,f1的基本屬性中沒有b,於是沿著__proto__找到了Foo.prototype.b。
那麼我們在實際應用中如何區分一個屬性到底是基本的還是從原型中找到的呢?大家可能都知道答案了——hasOwnProperty,特別是在for…in…迴圈中,一定要注意。
等等,不對! f1的這個hasOwnProperty方法是從哪裡來的? f1本身沒有,Foo.prototype中也沒有,哪兒來的?
好問題。
它是從Object.prototype中來的,請看圖:
物件的原型鏈是沿著__proto__這條線走的,因此在查詢f1.hasOwnProperty屬性時,就會順著原型鏈一直查詢到Object.prototype。
由於所有的物件的原型鏈都會找到Object.prototype,因此所有的物件都會有Object.prototype的方法。這就是所謂的“繼承”。
當然這只是一個例子,你可以自定義函式和物件來實現自己的繼承。
說一個函式的例子吧。
我們都知道每個函式都有call,apply方法,都有length,arguments,caller等屬性。為什麼每個函式都有?這肯定是“繼承”的。函式由Function函式建立,因此繼承的Function.prototype中的方法。不信可以請微軟的Visual Studio老師給我們驗證一下:
看到了吧,有call、length等這些屬性。
那怎麼還有hasOwnProperty呢?——那是Function.prototype繼承自Object.prototype的方法。有疑問可以看看上一節將instanceof時候那個大圖,看看Function.prototype.__proto__是否指向Object.prototype。
原型、原型鏈,大家都明白了嗎?
---------------------------------------------------------------------------
本文已更新到《深入理解javascript原型和閉包系列》的目錄,更多內容可參見《深入理解javascript原型和閉包系列》。
另外,歡迎關注我的微博。
學習作者教程:《前端JS高階面試》《前端JS基礎面試題》《React.js模擬大眾點評webapp》《zepto設計與原始碼分析》《json2.js原始碼解讀》