原型的基本概念
要想真正理解js的原型和原型鏈的概念沟蔑,必須且只要記住以下幾點(diǎn)即可:
? 一切都是對象(看似如此)秦爆。
undefined, number, string, boolean四種屬于簡單的值類型唇聘,不是對象懈词,使用基本類型變量可以調(diào)用方法是因?yàn)楫a(chǎn)生了包裝對象(臨時(shí)的)童叠。剩下的幾種情況——函數(shù)慎菲、數(shù)組嫁蛇、對象、null露该、new Number(10)都是對象睬棚,他們都是引用類型。
??所有的對象都是由函數(shù)創(chuàng)建解幼。
1抑党、函數(shù)也是一個(gè)對象,由Function函數(shù)創(chuàng)建撵摆。
2底靠、var obj = { a: 10, b: 20}; var arr = [5, 'x',true]; 這類定義其實(shí)只是一個(gè)下面的語法糖而已
3、Function也是一個(gè)對象特铝,由它自己創(chuàng)建暑中,有趣吧
? 所有的函數(shù)都有prototype屬性(原型)
注意壹瘟,是函數(shù)才有prototype,普通對象沒有鳄逾。
函數(shù)創(chuàng)建時(shí)就自動(dòng)帶有這個(gè)屬性稻轨,也就是我們講的“原型”,這也絕對是js中最基礎(chǔ)也是最難的部分雕凹。
這個(gè)prototype的屬性值是一個(gè)對象(屬性的集合殴俱,再次強(qiáng)調(diào)!)请琳,默認(rèn)的只有一個(gè)叫做constructor的屬性粱挡,指向這個(gè)函數(shù)本身。
prototype可以添加自定義屬性俄精,你可以試試Object.prototype询筏,可以看到很多自定義的屬性:
? 所有的對象都有__proto__。
1竖慧、所有的對象都有__proto__嫌套,指向創(chuàng)建它的函數(shù)的prototype,注意圾旨,你要這樣來理解這句話的意思踱讨,那就是同一個(gè)函數(shù)new出來的對象的__proto__都統(tǒng)一指向了這個(gè)函數(shù)的prototype,根據(jù)后面要講述的原型鏈規(guī)則砍的,也就是說通過這個(gè)函數(shù)new出來的所有對象都可以直接使用該函數(shù)原型上的任意屬性和方法痹筛!,因此廓鞠,對于jquery的這種形式就應(yīng)該能理解了
$是jQuery的簡寫別名帚稠,其實(shí)是一個(gè)函數(shù)。因此$div是jQuery函數(shù)創(chuàng)建的對象床佳,很顯然滋早,on方法就是在jQuery.prototype上定義的屬性(函數(shù)),因此所有jQuery函數(shù)創(chuàng)建的對象都已直接使用on方法
2砌们、所有的函數(shù)杆麸,比如 function fn(){},都是由Function函數(shù)創(chuàng)建浪感,因此fn的__proto__指向Function的prototype昔头。
3、比較有意思的是影兽,F(xiàn)unction也是函數(shù)揭斧,因此它也由Function創(chuàng)建的,也就是說它自己創(chuàng)建了自己赢笨!所有Function的__proto__指向的就是Function的prototype未蝌!
4、同理茧妒,Object函數(shù)也是由Function創(chuàng)建萧吠,因此Object的__proto__同樣指向Function的prototype!
5桐筏、prototype也是一個(gè)對象纸型,原始prototype只有一個(gè)叫做constructor的屬性,指向這個(gè)函數(shù)本身梅忌。因?yàn)閜rototype是一個(gè)對象狰腌,因此它也是由Object方法創(chuàng)建,因此它的__proto__將指向Object.prototype牧氮,如下所示:
6琼腔、但是Object.prototype卻是一個(gè)特例——它的__proto__指向的是null,切記切記踱葛!
因此丹莲,根據(jù)上面的幾條基本概念,從這段簡單的代碼我們可以畫出這樣一條關(guān)系鏈圖:
原型鏈
以上圖為例尸诽,我們來對原型鏈進(jìn)行描述甥材。
首先person是個(gè)函數(shù),我們在它的原型(prototype)上添加了一個(gè)getName的方法(函數(shù)屬性)
然后zs是person new出來的一個(gè)對象性含,因此zs的__proto__指向person的prototype洲赵。
person.prototype作為一個(gè)普通對象,是有Object函數(shù)創(chuàng)建的商蕴,因此它的__proto__指向Object.prototype
我們看到叠萍,zs對象本身沒有g(shù)etName方法,那它是怎么訪問到的究恤?
原來在當(dāng)前對象中沒有找到某個(gè)屬性時(shí)俭令,它會(huì)順著__proto__屬性依次向上查找,知道找到為止部宿!因此抄腔,
getName屬性在zs對象中沒有找到,就會(huì)繼續(xù)找zs.__proto__理张,也就是person.prototype赫蛇,很顯然,這里找到了雾叭,就不會(huì)再向上查找了
hasOwnProperty屬性顯然zs對象中沒有找到悟耘,就會(huì)繼續(xù)找zs.__proto__,也就是person.prototype织狐,很顯然暂幼,person.prototype中也找不到筏勒,于是繼續(xù)向上在person.prototype.__proto__中找。person.prototype是一個(gè)普通對象旺嬉,它是由Object方法創(chuàng)建的管行,因此person.prototype.__proto__就是Object.prototype,很顯然邪媳,Object.prototype里面已經(jīng)定義了hasOwnProperty方法(屬性)捐顷,因此在這里也找到了。
上面這種查找形式就成為原型鏈雨效。就像一根鏈條一樣迅涮,依次向上鏈接起來。這也是ES5及之前的所謂“繼承”實(shí)現(xiàn)徽龟。
我們注意到叮姑,在getName方法中是直接使用this.name來獲取zs對象的name值得,這就是說js在訪問原型對象的方法時(shí)据悔,直接把當(dāng)前對象應(yīng)用到了這個(gè)方法的上下文中戏溺。也就是相當(dāng)于:person.prototype.getName.apply(zs)
總結(jié)
要想正確理解掌握原型和原型鏈的概念,必須把上面講的最核心和基本的幾個(gè)概念理解和記住屠尊,否則看再多的案例也只會(huì)云里霧里旷祸,暈暈乎乎的,越加無法理解讼昆,靠死記硬背肯定是不行的托享。并且只要熟練掌握和牢記上面說的這幾個(gè)概念,不管遇到任何變著花樣的原型考查浸赫,都一定能夠正確理解闰围。