js原型鏈和繼承

我們會(huì)發(fā)現(xiàn) obj 已經(jīng)有幾個(gè)屬性(方法)了吁讨。那么問題來了:valueOf / toString / constructor 是怎么來第练?我們并沒有給 obj.valueOf 賦值呀雳刺。在理解繼承之前季惯,需要知道 js 的三個(gè)東西:

1仗考、什么是 JS 原型鏈

2、this 的值到底是什么

3请敦、JS 的 new 到底是干什么的

你可能遇到過這樣的 JS 面試題:

我們知道 JS 有對象,比如

var obj = {name:"obj"};

我們通過控制臺(tái)把 obj 打印出來:


我們會(huì)發(fā)現(xiàn) obj 已經(jīng)有幾個(gè)屬性(方法)了储玫。那么問題來了:valueOf / toString / constructor 是怎么來侍筛?我們并沒有給 obj.valueOf 賦值呀。

看如下示意圖

我們發(fā)現(xiàn)控制臺(tái)打出來的結(jié)果是:

1撒穷、obj 本身有一個(gè)屬性 name (這是我們給它加的)

2匣椰、obj 還有一個(gè)屬性叫做proto(它是一個(gè)對象)

3、obj 還有一個(gè)屬性端礼,包括 valueOf, toString, constructor 等

4禽笑、obj.proto其實(shí)也有一個(gè)叫做proto的屬性(console.log 沒有顯示)入录,值為 null

現(xiàn)在回到我們的問題:obj 為什么會(huì)擁有 valueOf / toString / constructor 這幾個(gè)屬性?

答案: 這跟?proto有關(guān) 佳镜。

當(dāng)我們「讀取」 obj.toString 時(shí)僚稿,JS 引擎會(huì)做下面的事情:

1、看看 obj 對象本身有沒有 toString 屬性蟀伸。沒有就走到下一步蚀同。

2、看看 obj.proto對象有沒有 toString 屬性啊掏, 發(fā)現(xiàn) obj.proto有 toString 屬性蠢络, 于是找到了,所以 obj.toString 實(shí)際就是第 2 步中找到的 obj.proto.toString脖律。

3谢肾、如果 obj.proto沒有,那么瀏覽器會(huì)繼續(xù)查看 obj.proto.proto

4小泉、如果 obj.proto.proto也沒有芦疏,那么瀏覽器會(huì)繼續(xù)查看 obj.proto.proto.proto

5、直到找到 toString 或者proto為 null微姊。

上面的過程酸茴,就是「讀」屬性的「搜索過程」。而這個(gè)「搜索過程」兢交,是連著由?proto?組成的鏈子一直走的薪捍。這個(gè)鏈子,就叫做「原型鏈」配喳。

共享原型鏈

現(xiàn)在我們還有另一個(gè)對象

var????obj2 =? ? {name:"obj2"}

那么 obj.toString 和 obj2.toString 其實(shí)是同一東西酪穿, 也就是 obj2.proto.toString。

說白了晴裹,我們改其中的一個(gè)proto.toString 被济,那么另外一個(gè)其實(shí)也會(huì)變!

差異化

如果我們想讓 obj.toString 和 obj2.toString 的行為不同怎么做呢?

直接賦值就好了:

obj.toString = function () {?

????return "新的 toString 方法";

};

小結(jié)

????[讀]屬性時(shí)會(huì)沿著原型鏈搜索

????[新增]屬性時(shí)不會(huì)去看原型鏈

2. this 的值到底是什么

你可能遇到過這樣的 JS 面試題:

var obj = {

? ? foo:function(){console.log(this);? ??

}};

var bar = obj.foo;

obj.foo();// 打印出的 this 是 obj

bar();// 打印出的 this 是 window

函數(shù)調(diào)用

JS(ES5)里面有三種函數(shù)調(diào)用形式:

1涧团、func(p1, p2);

2只磷、obj.child.method(p1, p2);

3、func.call(context, p1, p2);// 先不講 apply

前面兩種都是語法糖泌绣,可以等價(jià)地變?yōu)?call 形式:

func(p1,p2)等價(jià)于?func.call(undefined, p1, p2);

obj.child.method(p1, p2) 等價(jià)于 obj.child.method.call(obj.child, p1, p2);

this 是你 call 一個(gè)函數(shù)時(shí)傳的 context钮追,先看 func(p1, p2) 中的 this 如何確定:

按理說打印出來的 this 應(yīng)該就是 undefined 了吧,但是瀏覽器里有一條規(guī)則:

????如果你傳的 context 就 null 或者 undefined阿迈,那么 window 對象就是默認(rèn)的 context(嚴(yán)格模式下默認(rèn) context 是 undefined)

因此上面的打印結(jié)果是 window元媚。如果你希望這里的 this 不是 window,很簡單:

func.call(obj)// 那么里面的 this 就是 obj 對象了

回到題目:

[ ] 語法

我們可以把 arr0想象為 arr.0( ),雖然后者的語法錯(cuò)了惠毁,但是形式與轉(zhuǎn)換代碼里的 obj.child.method(p1, p2) 對應(yīng)上了犹芹,于是就可以愉快的轉(zhuǎn)換了:

arr[0]();????假想為 arr.0()????然后轉(zhuǎn)換為 arr.0.call(arr)????那么里面的 this 就是 arr 了?

小結(jié):

????this 就是你 call 一個(gè)函數(shù)時(shí),傳入的第一個(gè)參數(shù)鞠绰。

????如果你的函數(shù)調(diào)用不是 call 形式腰埂, 請將其轉(zhuǎn)換為 call 形式

3. JS 的 new 到底是干什么的?

先看一下問題

用一個(gè)函數(shù)把這兩部分聯(lián)系起來:

JS 之父看到大家都這么搞蜈膨,覺得何必呢屿笼,我給你們個(gè)糖吃,于是 JS 之父創(chuàng)建了 new 關(guān)鍵字翁巍,可以讓我們少寫幾行代碼:

只要你在士兵前面使用 new 關(guān)鍵字驴一,那么可以少做四件事情:

1.不用創(chuàng)建臨時(shí)對象,因?yàn)?new 會(huì)幫你做(你使用「this」就可以訪問到臨時(shí)對象)灶壶;

2.不用綁定原型肝断,因?yàn)?new 會(huì)幫你做(new 為了知道原型在哪,所以指定原型的名字 prototype);

3.不用 return 臨時(shí)對象驰凛,因?yàn)?new 會(huì)幫你做胸懈;

4.不要給原型想名字了,因?yàn)?new 指定名字為 prototype恰响。

注意 constructor 屬性

new 操作為了記錄「臨時(shí)對象是由哪個(gè)函數(shù)創(chuàng)建的」趣钱,所以預(yù)先給「士兵.prototype」加了一個(gè) constructor 屬性:

士兵.prototype = {

? ? constructor: 士兵

};

如果你重新對「士兵.prototype」賦值,那么這個(gè) constructor 屬性就沒了胚宦,所以你應(yīng)該這么寫:

或者你也可以自己給 constructor 重新賦值:

四首有、繼承

繼承的本質(zhì)就是上面的講的原型鏈

1)借助構(gòu)造函數(shù)實(shí)現(xiàn)繼承

打印結(jié)果

這個(gè)主要是借用 call 來改變 this 的指向,通過 call 調(diào)用 Parent 枢劝,此時(shí) Parent 中的 this 是指 Child1井联。有個(gè)缺點(diǎn),從打印結(jié)果看出 Child1 并沒有 say 方法您旁,所以這種只能繼承父類的實(shí)例屬性和方法低矮,不能繼承原型屬性/方法。

2)借助原型鏈實(shí)現(xiàn)繼承

要共享莫些屬性被冒,需要 對象.proto?= 父親對象的.prototype,但實(shí)際上我們是不能直接 操作proto坪仇,這時(shí)我們可以借用 new 來做公浪,所以

Child2.prototype = new Parent2(); <=> Child2.prototype.proto?= Parent2.prototype; 這樣我們借助 new 這個(gè)語法糖,就可以實(shí)現(xiàn)原型鏈繼承妓灌。但這里有個(gè)總是跃洛,如打印結(jié)果率触,我們給 s1.play 新增一個(gè)值 ,s2 也跟著改了汇竭。所以這個(gè)是原型鏈繼承的缺點(diǎn)葱蝗,原因是 s1.pro?和 s2.pro指向同一個(gè)地址即 父類的 prototype穴张。

3)組合方式實(shí)現(xiàn)繼承

將 1 和 2 兩種方式組合起來,就可以解決 1 和 2 存在問題两曼,這種方式為組合繼承皂甘。這種方式有點(diǎn)缺點(diǎn)就是我實(shí)例一個(gè)對象的時(shí), 父類 new 了兩次悼凑,一次是 var s3 = new Child3()對應(yīng) Child3.prototype = new Parent3()還要 new 一次偿枕。

4)組合繼承的優(yōu)化 1


這邊主要為 Child4.prototype = Parent4.prototype, 因?yàn)槲覀兺ㄟ^構(gòu)造函數(shù)就可以拿到所有屬性和實(shí)例的方法户辫,那么現(xiàn)在我想繼承父類的原型對象渐夸,所以你直接賦值給我就行,不用在去 new 一次父類渔欢。其實(shí)這種方法還是有問題的墓塌,如果我在控制臺(tái)打印以下兩句:

從打印可以看出,此時(shí)我是沒有辦法區(qū)分一個(gè)對象 是直接 由它的子類實(shí)例化還是父類呢奥额?我們還有一個(gè)方法判斷來判斷對象是否是類的實(shí)例苫幢,那就是用 constructor,我在控制臺(tái)打印以下內(nèi)容:

咦,你會(huì)發(fā)現(xiàn)它指向的是父類 披坏,這顯然不是我們想要的結(jié)果态坦, 上面講過我們 prototype 里面有一個(gè) constructor, 而我們此時(shí)子類的 prototype 指向是 父類的 prototye ,而父類 prototype 里面的 contructor 當(dāng)然是父類自己的,這個(gè)就是產(chǎn)生該問題的原因棒拂。

組合繼承的優(yōu)化 2

這里主要使用Object.create()伞梯,它的作用是將對象繼承到proto屬性上。舉個(gè)例子:

那大家可能說這樣解決了嗎帚屉,其實(shí)沒有解決,因?yàn)檫@時(shí) Child5.prototype 還是沒有自己的 constructor,它要找的話還是向自己的原型對象上找最后還是找到 Parent5.prototype, constructor 還是 Parent5 ,所以要給 Child5.prototype 寫自己的 constructor:

Child5.prototype = Object.create(Parent5.prototype);

Child5.prototype.constructor = Child5;

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末谜诫,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子攻旦,更是在濱河造成了極大的恐慌喻旷,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,406評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件牢屋,死亡現(xiàn)場離奇詭異且预,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)烙无,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,732評論 3 393
  • 文/潘曉璐 我一進(jìn)店門锋谐,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人截酷,你說我怎么就攤上這事涮拗。” “怎么了?”我有些...
    開封第一講書人閱讀 163,711評論 0 353
  • 文/不壞的土叔 我叫張陵三热,是天一觀的道長鼓择。 經(jīng)常有香客問我,道長就漾,這世上最難降的妖魔是什么呐能? 我笑而不...
    開封第一講書人閱讀 58,380評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮从藤,結(jié)果婚禮上催跪,老公的妹妹穿的比我還像新娘。我一直安慰自己夷野,他們只是感情好懊蒸,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,432評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著悯搔,像睡著了一般骑丸。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上妒貌,一...
    開封第一講書人閱讀 51,301評論 1 301
  • 那天通危,我揣著相機(jī)與錄音,去河邊找鬼灌曙。 笑死菊碟,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的在刺。 我是一名探鬼主播逆害,決...
    沈念sama閱讀 40,145評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼蚣驼!你這毒婦竟也來了魄幕?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,008評論 0 276
  • 序言:老撾萬榮一對情侶失蹤颖杏,失蹤者是張志新(化名)和其女友劉穎纯陨,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體留储,經(jīng)...
    沈念sama閱讀 45,443評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡翼抠,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,649評論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了获讳。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片机久。...
    茶點(diǎn)故事閱讀 39,795評論 1 347
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖赔嚎,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤尤误,帶...
    沈念sama閱讀 35,501評論 5 345
  • 正文 年R本政府宣布侠畔,位于F島的核電站,受9級(jí)特大地震影響损晤,放射性物質(zhì)發(fā)生泄漏软棺。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,119評論 3 328
  • 文/蒙蒙 一尤勋、第九天 我趴在偏房一處隱蔽的房頂上張望喘落。 院中可真熱鬧,春花似錦最冰、人聲如沸瘦棋。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,731評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽赌朋。三九已至,卻和暖如春篇裁,著一層夾襖步出監(jiān)牢的瞬間沛慢,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,865評論 1 269
  • 我被黑心中介騙來泰國打工达布, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留团甲,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,899評論 2 370
  • 正文 我出身青樓黍聂,卻偏偏與公主長得像躺苦,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子分冈,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,724評論 2 354

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