震驚!原來你是這樣的原型繼承熊杨。曙旭。。

成為一名前端工程師差不多有一年了晶府,仍然對原型繼承的概念一知半解,期間查看網(wǎng)上許多資料钻趋,還是沒有建立對原型繼承建立起完全的知識鏈川陆。直到看了《JavaScript語言精粹與編程實踐》一書,才對原型繼承有了一個全面的認(rèn)識蛮位。在本書中作者提出的觀點顛覆了我對原型繼承的認(rèn)識较沪,所以在本篇文章我將對原型繼承的知識進(jìn)行歸納總結(jié)鳞绕,力求全面而又準(zhǔn)確的介紹原型繼承。

原型繼承是JavaScript最重要的語言特性之一尸曼,接下來將從對象形成過程和構(gòu)造過程詳細(xì)講解原型繼承们何。

空的對象是所有對象的基礎(chǔ)

在展開整個話題之前,我們必須要先從最基本的Object()構(gòu)造器開始控轿。

obj3 = Object.prototype;
var num = 0;
for(var i in obj){
  num++;
}
console.log(num); //0

上面的代碼已經(jīng)明確的展示出Object()構(gòu)造器的原型obj3就是一個空的對象冤竹。可能有人對空的對象不是很了解茬射,所以鹦蠕,在這里簡單介紹下空的對象≡谂祝空的對象實質(zhì)上只是滿足以下條件的數(shù)據(jù)結(jié)構(gòu):

  • __proto__屬性指向Object.prototype
  • 其成員列表指向一個空殼

所謂的“空的對象”就是一個標(biāo)準(zhǔn)的通過Object()構(gòu)造出來的對象實例钟病。下面例子中的obj1和obj2都是空的對象。

obj1 = new Object();
obj2 = {};

現(xiàn)在刚梭,讓我們回到Object()構(gòu)造器的原型肠阱,我們說到obj3是一個空的對象,而obj1和obj2也是一個空的對象朴读,那么是不是可以認(rèn)為對象的形成就是一個簡單的復(fù)制過程屹徘,就如同下面這張圖片描述一樣。

對象的形成.png

但是如果真的如上面這張途中所描述的那種磨德,每次構(gòu)造一個實例都從原型中復(fù)制出一個實例來的話缘回,那么內(nèi)存的占用的消耗會急速增加,這樣會對性能產(chǎn)生影響典挑。所以JavaScript僅當(dāng)寫某個實例的成員時酥宴,將成員的信息復(fù)制到實例的映像中,也就是當(dāng)需要寫對象屬性的時候(obj12.value=10)您觉,會產(chǎn)生一個名為value的屬性值拙寡,放在obj2對象的成員列表中。所以相對于obj1琳水,obj2就多出了一張成員列表肆糕,但是obj2依舊是一個指向原型的引用,下面這張圖很清楚的描述了obj2對象在孝。

對象.png

在此補(bǔ)充一個小知識點诚啃,對象的成員的存取遵循兩個規(guī)則:1、在讀取時私沮,該成員表上的屬性和方法將會被優(yōu)先訪問到始赎;2、如果成員表中沒有指定的屬性,那么會查詢對象的整個原型鏈造垛,直到找到該屬性或者原型鏈的頂部魔招。所以存取實例中的屬性比存取原型中的屬性效率要高。

在這里需要特別指出的是:Object.prototype處于原型鏈的頂部(盡管有些文章認(rèn)為null處于原型鏈的頂端五辽,因為事實上Object.prototype.__proto__===null,但是null一無所有办斑,其跟空的對象不同,空的對象里面還有預(yù)定義的屬性和方法杆逗,而null里面就是空的乡翅,其是一個特殊的對象,從原型繼承的角度上看髓迎,對象根本不可能從null中繼承到什么峦朗,所以null并不是原型鏈的頂部,而Object.prototype才被認(rèn)為處于原型鏈的頂部)排龄,因此所有的對象均從Object.prototype中繼承預(yù)定義的屬性和方法波势,而不是從null中繼承,所以空的對象才可以是所有對象的基礎(chǔ)橄维。

從函數(shù)到構(gòu)造器

在上面我們分析了對象的形成過程尺铣,并沒有解釋函數(shù)作為構(gòu)造函數(shù)到底發(fā)生了什么,所以在這將分析函數(shù)到構(gòu)造器的構(gòu)造過程争舞。同上面對象的形成一樣凛忿,如果每次聲明一個函數(shù)的時候,都會先創(chuàng)建一個對象實例竞川,之后將函數(shù)中的prototype成員指向該對象實例店溢,那將是非常不經(jīng)濟(jì)。那么JavaScript是怎么操作的委乌?

在JavaScript內(nèi)部床牧,構(gòu)造函數(shù)跟普通函數(shù)并沒有區(qū)別,二者在聲明時遭贸,其prototype值是null戈咳,只有在需要引用到原型的時候(即通過new關(guān)鍵字調(diào)用進(jìn)行創(chuàng)建對象實例),才具有構(gòu)造器的特性壕吹,所以在JavaScript內(nèi)部的實現(xiàn)很有可能是如下代碼所示:

//設(shè)定__proto__是函數(shù)內(nèi)置的方法著蛙,get_prototype()是它的讀方法
var __proto__ = null;
function get_prototype(){
    if(!__proto__){
        __proto__ = new Object();
        __proto__.constructor = this;
    };
    return __proto__;
};

在上面的代碼可以清晰的發(fā)現(xiàn)函數(shù)的原型是一個標(biāo)準(zhǔn)的、系統(tǒng)內(nèi)置的Object()構(gòu)造器的一個實例耳贬,當(dāng)該實例構(gòu)建完成后其constructor屬性被賦值為當(dāng)前函數(shù)踏堡。這點非常好證明,因為可以使用delete運算符刪去當(dāng)前的屬性咒劲,讓成員取到父類的屬性值暂吉。

function MyObject(){

};
console.log(MyObject.prototype.constructor == MyObject); //true
delete MyObject.prototype.constructor;
console.log(MyObject.prototype.constructor == Object); //true
console.log(MyObject.prototype.constructor == new Object().constructor);// true

所有MyObject.prototype實際上與一個普通對象并沒有本質(zhì)的區(qū)別胖秒,當(dāng)一個函數(shù)的prototype有意義之后缎患,其就變成了一個“構(gòu)造器”慕的。事實上,我們可以假設(shè)構(gòu)造器的prototype屬性總是來自于new Object()產(chǎn)生的實例挤渔,這個假設(shè)對我們以后理解“動態(tài)語言特性”有非常大的幫助肮街。

從構(gòu)造過程,我們知道了JavaScript的實例對象實際上是一個指向其原型的判导,并持有一個成員列表的結(jié)構(gòu)嫉父。聯(lián)想到之前空的對象的實質(zhì),我們不難推斷出:所有的實例對象的共同原型Object.prototype具有某些性質(zhì)眼刃,而使這些實例對象具有對象的某些性質(zhì)绕辖。
如果對對象實例的性質(zhì)做一個分類的話,可以劃分成:

成員名 類型 分類
tostring function 動態(tài)語言
toLocaleString function 動態(tài)語言
valueOf function 動態(tài)語言
constructor function 對象系統(tǒng):構(gòu)造
propertyIsEnumerable function 對象系統(tǒng):屬性
hasOwnProperty function 對象系統(tǒng):屬性
isPrototypeOf function 對象系統(tǒng):原型

但是對于一個構(gòu)造函數(shù)而言擂红,其還具有一些特殊的屬性仪际,那就是原型prototype

原型鏈的維護(hù)

從構(gòu)造器的特殊屬性prototype來看昵骤,似乎只有構(gòu)造函數(shù)才能維護(hù)原型鏈树碱。從之前的討論來看,一個實例對象應(yīng)該擁有一個指向原型的__proto__屬性(即實例對象的__proto__指向構(gòu)造其的構(gòu)造函數(shù)的prototype)变秦,該屬性是不可見的成榜,但是在某些瀏覽器中以__proto__的形式將該屬性暴露出來供開發(fā)者調(diào)試,該屬性也被稱之為“內(nèi)部原型鏈”(該原型鏈才是真正的原型鏈)蹦玫,其與構(gòu)造函數(shù)的prototype所組成的“構(gòu)造器原型鏈”共同組成JavaScript的原型鏈赎婚。下面來看一個示例:

function MyObject(){

};
function MyObjectEx(){

};
//構(gòu)造器原型鏈
MyObjectEx.prototype = new MyObject();
var obj1 = new MyObjectEx();
var obj2 = new MyObjectEx();

下圖展示了代碼所構(gòu)成的內(nèi)部原型鏈與構(gòu)造原型鏈:

原型鏈.png

該圖構(gòu)造器通過ptototype構(gòu)建了一個原型鏈,對象實例通過__proto__構(gòu)建了一個原型鏈樱溉,但是由于__proto__不可訪問挣输,所以沒有辦法從對象實例開始訪問整個原型鏈。這時候饺窿,constructor就閃亮登場了歧焦。

constructor

function MyObject(){

};
var obj1 = new MyObject();
//構(gòu)造函數(shù)的原型的constructor屬性指向了構(gòu)造器本身
console.log(MyObject.prototype.constructor===MyObject);//true
//在JS中,一個實例對象的constructor屬性總是指向構(gòu)造函數(shù)肚医。
console.log(obj.construcrot===MyObject);//true

所以绢馍,經(jīng)過上面的代碼,我們發(fā)現(xiàn)了一個連接點肠套,通過constructor可以讓實例對象訪問構(gòu)造函數(shù)的原型鏈舰涌。經(jīng)過了constructor的連接,這個原型鏈的連接就變成如下所示:

原型鏈的連接.png

既然我們只需要使用正確的constructor屬性就可以使用整個原型鏈你稚,那么其內(nèi)部原型鏈有什么用瓷耙。這就要提到原型繼承的實質(zhì)了朱躺。面向?qū)ο蟮睦^承性明確約定了:子類與父類具有相似性。在原型繼承中搁痛,相似性是在構(gòu)造的時候決定的长搀,也是由new運算內(nèi)部的那個“復(fù)制”操作決定的。所以__proto__屬性就是為了保證這種一致性鸡典,也可以這樣說內(nèi)部原型鏈?zhǔn)荍avaScript的原型繼承機(jī)制所決定源请。

所以在這里簡單總結(jié)下構(gòu)造函數(shù)、原型彻况、隱式原型和實例的關(guān)系:每個構(gòu)造函數(shù)都有一個原型屬性(prototype)谁尸,該屬性指向構(gòu)造函數(shù)的原型對象;而實例對象有一個隱式原型屬性(__proto__)纽甘,其指向構(gòu)造函數(shù)的原型對象(obj.__proto__==Object.prototype)良蛮;同時實例對象的原型對象中有一個constructor屬性,其指向構(gòu)造函數(shù)悍赢。(該關(guān)系用文字描述有點干澀决瞳,所以可能會在下篇文章中說明這些問題)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市泽裳,隨后出現(xiàn)的幾起案子瞒斩,更是在濱河造成了極大的恐慌,老刑警劉巖涮总,帶你破解...
    沈念sama閱讀 206,482評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件胸囱,死亡現(xiàn)場離奇詭異,居然都是意外死亡瀑梗,警方通過查閱死者的電腦和手機(jī)烹笔,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,377評論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來抛丽,“玉大人谤职,你說我怎么就攤上這事∫谙剩” “怎么了允蜈?”我有些...
    開封第一講書人閱讀 152,762評論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長蒿柳。 經(jīng)常有香客問我饶套,道長,這世上最難降的妖魔是什么垒探? 我笑而不...
    開封第一講書人閱讀 55,273評論 1 279
  • 正文 為了忘掉前任妓蛮,我火速辦了婚禮,結(jié)果婚禮上圾叼,老公的妹妹穿的比我還像新娘蛤克。我一直安慰自己捺癞,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 64,289評論 5 373
  • 文/花漫 我一把揭開白布构挤。 她就那樣靜靜地躺著髓介,像睡著了一般。 火紅的嫁衣襯著肌膚如雪儿倒。 梳的紋絲不亂的頭發(fā)上版保,一...
    開封第一講書人閱讀 49,046評論 1 285
  • 那天,我揣著相機(jī)與錄音夫否,去河邊找鬼。 笑死叫胁,一個胖子當(dāng)著我的面吹牛凰慈,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播驼鹅,決...
    沈念sama閱讀 38,351評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼微谓,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了输钩?” 一聲冷哼從身側(cè)響起豺型,我...
    開封第一講書人閱讀 36,988評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎买乃,沒想到半個月后姻氨,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,476評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡剪验,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,948評論 2 324
  • 正文 我和宋清朗相戀三年肴焊,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片功戚。...
    茶點故事閱讀 38,064評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡娶眷,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出啸臀,到底是詐尸還是另有隱情届宠,我是刑警寧澤,帶...
    沈念sama閱讀 33,712評論 4 323
  • 正文 年R本政府宣布乘粒,位于F島的核電站豌注,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏谓厘。R本人自食惡果不足惜幌羞,卻給世界環(huán)境...
    茶點故事閱讀 39,261評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望竟稳。 院中可真熱鬧属桦,春花似錦熊痴、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,264評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至系谐,卻和暖如春巾陕,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背纪他。 一陣腳步聲響...
    開封第一講書人閱讀 31,486評論 1 262
  • 我被黑心中介騙來泰國打工鄙煤, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人茶袒。 一個月前我還...
    沈念sama閱讀 45,511評論 2 354
  • 正文 我出身青樓梯刚,卻偏偏與公主長得像,于是被迫代替她去往敵國和親薪寓。 傳聞我的和親對象是個殘疾皇子亡资,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,802評論 2 345

推薦閱讀更多精彩內(nèi)容