js原型鏈(看了一個大神 的部落格,覺得真的是挖到了寶藏一樣)
原型的基本概念
要想真正理解js的原型和原型鏈的概念,必須且只要記住以下幾點即可:
▶ 一切都是物件(看似如此)。
undefined, number, string, boolean四種屬於簡單的值型別,不是物件,使用基本型別變數可以呼叫方法是因為產生了包裝物件(臨時的)。剩下的幾種情況——函式、陣列、物件、null、new Number(10)都是物件,他們都是引用型別。
▶ 所有的物件都是由函式建立。
1、函式也是一個物件,由Function函式建立。
2、var obj = { a: 10, b: 20}; var arr = [5, 'x',true]; 這類定義其實只是一個下面的語法糖而已
3、Function也是一個物件,由它自己建立,有趣吧
▶ 所有的函式都有prototype屬性(原型)
注意,是函式才有prototype,普通物件沒有。
函式建立時就自動帶有這個屬性,也就是我們講的“原型”,這也絕對是js中最基礎也是最難的部分。
這個prototype的屬性值是一個物件(屬性的集合,再次強調!),預設的只有一個叫做constructor的屬性,指向這個函式本身。
prototype可以新增自定義屬性,你可以試試Object.prototype,可以看到很多自定義的屬性:
▶ 所有的物件都有__proto__。
1、所有的物件都有__proto__,指向建立它的函式的prototype,注意,你要這樣來理解這句話的意思,那就是同一個函式new出來的物件的__proto__都統一指向了這個函式的prototype,根據後面要講述的原型鏈規則,也就是說通過這個函式new出來的所有物件都可以直接使用該函式原型上的任意屬性和方法!,
$是jQuery的簡寫別名,其實是一個函式。因此$div是jQuery函式建立的物件,很顯然,on方法就是在jQuery.prototype上定義的屬性(函式),因此所有jQuery函式建立的物件都已直接使用on方法
2、所有的函式,比如 function fn(){},都是由Function函式建立,因此fn的__proto__指向Function的prototype。
3、比較有意思的是,Function也是函式,因此它也由Function建立的,也就是說它自己建立了自己!所有Function的__proto__指向的就是Function的prototype!
4、同理,Object函式也是由Function建立,因此Object的__proto__同樣指向Function的prototype!
5、prototype也是一個物件,原始prototype只有一個叫做constructor的屬性,指向這個函式本身。因為prototype是一個物件,因此它也是由Object方法建立,因此它的__proto__將指向Object.prototype,如下所示:
6、但是Object.prototype卻是一個特例——它的__proto__指向的是null,切記切記!
想想也覺得應該是這樣吧
因此,根據上面的幾條基本概念,從這段簡單的程式碼我們可以畫出這樣一條關係鏈圖:
原型鏈
以上圖為例,我們來對原型鏈進行描述。
首先person是個函式,我們在它的原型(prototype)上添加了一個getName的方法(函式屬性)
然後zs是person new出來的一個物件,因此zs的__proto__指向person的prototype。
person.prototype作為一個普通物件,是有Object函式建立的,因此它的__proto__指向Object.prototype
我們看到,zs物件本身沒有getName方法,那它是怎麼訪問到的?
原來在當前物件中沒有找到某個屬性時,它會順著__proto__屬性依次向上查詢,知道找到為止!因此,
getName屬性在zs物件中沒有找到,就會繼續找zs.__proto__,也就是person.prototype,很顯然,這裡找到了,就不會再向上查找了
hasOwnProperty屬性顯然zs物件中沒有找到,就會繼續找zs.__proto__,也就是person.prototype,很顯然,person.prototype中也找不到,於是繼續向上在person.prototype.__proto__中找。person.prototype是一個普通物件,它是由Object方法建立的,因此person.prototype.__proto__就是Object.prototype,很顯然,Object.prototype裡面已經定義了hasOwnProperty方法(屬性),因此在這裡也找到了。
上面這種查詢形式就成為原型鏈。就像一根鏈條一樣,依次向上連結起來。這也是ES5及之前的所謂“繼承”實現。
原型鏈訪問順序
我們注意到,在getName方法中是直接使用this.name來獲取zs物件的name值得,這就是說js在訪問原型物件的方法時,直接把當前物件應用到了這個方法的上下文中。也就是相當於:person.prototype.getName.apply(zs)
總結
要想正確理解掌握原型和原型鏈的概念,必須把上面講的最核心和基本的幾個概念理解和記住,否則看再多的案例也只會雲裡霧裡,暈暈乎乎的,越加無法理解,靠死記硬背肯定是不行的。並且只要熟練掌握和牢記上面說的這幾個概念,不管遇到任何變著花樣的原型考查,都一定能夠正確理解。