Javascript 面向?qū)ο蟮某绦蛟O(shè)計(jì)(原型鏈與繼承)

繼承

原型鏈

講原型的時(shí)候提到過(guò)繼承,設(shè)計(jì)原型的初衷就是為了繼承,原型鏈?zhǔn)菍?shí)現(xiàn)繼承的主要方法监署。
那什么是原型鏈颤专,還記得之前提到過(guò)的作用域鏈嗎,它表示標(biāo)識(shí)符在環(huán)境中的查找順序钠乏,原型鏈與作用域鏈相似栖秕,它表示屬性或方法在實(shí)例和構(gòu)造函數(shù)間的追溯順序,看一個(gè)例子:

function Father() {
}
Father.prototype.familyName = "Zhao";
function Child() {
}
var father = new Father();
Child.prototype = father;
var me = new Child();
me.familyName;
// "Zhao"


當(dāng)我們?cè)?code>me實(shí)例上訪問(wèn)familyName屬性時(shí)缓熟,搜索過(guò)程會(huì)從原型鏈的末端開(kāi)始逐步向上累魔,即:
me實(shí)例 → Child原型 → Father原型

默認(rèn)的原型

由于所有引用類型都繼承了Object 因此所有引用類型原型鏈的頂端都是Object.prototype,因此所有自定義類型都會(huì)繼承toString() valueOf()等默認(rèn)方法够滑。

確定原型和實(shí)例的關(guān)系
  1. instanceof
    用這個(gè)操作符測(cè)試實(shí)例和原型鏈上出現(xiàn)的構(gòu)造函數(shù)垦写,即返回true
  2. isPropertyOf
    用這個(gè)方法測(cè)試原型鏈中出現(xiàn)過(guò)的原型,即返回true
謹(jǐn)慎地定義方法

在上面的例子中彰触,我們有一步替換原型對(duì)象的操作:

Child.prototype = father;

很多時(shí)候梯投,我們想要在子類型中添加一些超類型沒(méi)有的方法,應(yīng)該放在替換原型對(duì)象之后况毅。

不能使用對(duì)象字面量

通過(guò)原型鏈實(shí)現(xiàn)繼承時(shí)分蓖,不能使用對(duì)象字面量來(lái)重寫(xiě)原型,因此這樣做會(huì)切斷子類型與超類型之間的聯(lián)系尔许。

原型鏈的問(wèn)題

在上述例子中么鹤,Child構(gòu)造函數(shù)的原型是Father的實(shí)例father,那么father實(shí)例的屬性就變成Child的原型屬性味廊,接下來(lái)在Child的所有實(shí)例蒸甜,均會(huì)共享father實(shí)例的屬性,我們修改一下上述例子:

function Father() {
    this.hobbies = [];
}
function Child() {
}
Child.prototype = new Father();
var me = new Child();
var sister = new Child();
me.hobbies.push("dancing");
sister.hobbies;
// ["dancing"]

me修改Child原型上的hobbies屬性會(huì)影響到sister訪問(wèn)該屬性時(shí)獲得的值余佛,注意柠新,當(dāng)我們直接在實(shí)例對(duì)象上對(duì)某個(gè)屬性賦值時(shí),我們相當(dāng)于修改或添加實(shí)例中的某個(gè)屬性(同訪問(wèn)不一樣辉巡,不遵循原型鏈)恨憎,比如:

me.hobbies = [];
sister.hobbies;
//["dancing"]

上述例子中,直接在me實(shí)例中添加屬性hobbies郊楣,因此沒(méi)有影響sister憔恳。

借用構(gòu)造函數(shù)

即在子類型的構(gòu)造函數(shù)調(diào)用超類型的構(gòu)造函數(shù),舉一個(gè)例子:

function Father(givenName) {
    this.givenName = givenName;
}
function Child(givenName) {
    Father.call(this, givenName)
}
var child = new Child("Xianshu");
child.givenName
// "Xianshu"
  1. 私有屬性
    使用這種方式痢甘,可以使每個(gè)實(shí)例從超類型中繼承的屬性私有化喇嘱,一個(gè)實(shí)例更改屬性值不會(huì)影響到其他屬性,舉一個(gè)例子:
var child1 = new Child("Sue")
var child2 = new Child("Jane")
child1.givenName
// "Sue"
child2.givenName
// "Jane"
  1. 傳遞參數(shù)
    通過(guò)這種方式塞栅,子類型可以在調(diào)用超類型構(gòu)造函數(shù)時(shí)向其傳參者铜。

  2. 結(jié)構(gòu)構(gòu)造函數(shù)的問(wèn)題
    首先沒(méi)辦法在實(shí)例間復(fù)用函數(shù)腔丧,比如:

function Father(givenName) {
    this.givenName = givenName;
    this.cook = function() {
        return "delicious food";
    }
}
function Child(givenName) {
    Father.call(this, givenName)
}
var child1 = new Child("Sue")
var child2 = new Child("Jane")
child1.cook === child2.cook
// false

上述例子中,可以看出作烟,兩個(gè)實(shí)例的cook方法引用的不是同一塊內(nèi)存地址愉粤,說(shuō)明cook被實(shí)例化了多次,這顯然是冗余的拿撩。
其次衣厘,超類型的原型中定義的方法對(duì)于子類型來(lái)說(shuō)也是不可見(jiàn)的。

組合繼承

使用原型鏈來(lái)實(shí)現(xiàn)方法的繼承压恒,使用構(gòu)造函數(shù)實(shí)現(xiàn)屬性的繼承影暴。比如:

function Father(givenName) {
    this.givenName = givenName;
}
Father.prototype.cook = function() {
    return "delicious food";
}
function Child(givenName) {
    Father.call(this, givenName)
}
Child.prototype = new Father();
Child.prototype.constructor = Child;
var child = new Child("Sue")
child.cook();
// "delicious food"

在創(chuàng)建Child構(gòu)造函數(shù)時(shí),首先繼承超類型Father中的屬性探赫,然后創(chuàng)建一個(gè)Father實(shí)例型宙,將其賦值給Child的原型,使其繼承Father定義在原型中的方法伦吠,這里需要注意兩點(diǎn):

  1. new Father()中的givenName屬性不會(huì)覆蓋child中的givenName屬性妆兑,這是由標(biāo)識(shí)符在實(shí)例及其原型鏈上的搜索順序決定的
  2. 需要修正Child.prototype.constructor屬性

原型式繼承

無(wú)需構(gòu)造函數(shù)的一種繼承方式,在一個(gè)對(duì)象的基礎(chǔ)上創(chuàng)建出一個(gè)新對(duì)象:

function object(o) {
    function F(){}
    F.prototype = o;
    return new F();
}
var person = { age : 18, hobbies: [] };
var anotherPerson = object(person);
anotherPerson.age;
// 18

上述例子中毛仪,person不是構(gòu)造函數(shù)搁嗓,只是一個(gè)普通的對(duì)象,anotherPerson繼承了它的屬性箱靴,需要注意的是腺逛,繼承到的屬性是在實(shí)例間共享的。

anotherPerson.hobbies.push("dancing");
anotherPerson.age = 19;
var anotherPerson2 = object(person);
anotherPerson2.age; // 18
anotherPerson2.hobbies; // ["dancing"]

寄生式繼承

寄生式繼承封裝了如下過(guò)程:

  1. 調(diào)用一個(gè)能夠返回新對(duì)象的函數(shù)
  2. 以某種方式來(lái)增強(qiáng)返回的對(duì)象
  3. 返回對(duì)象
function printBook(original) {
    var book = object(original);
    book.auther = "Sue";
    return book;
}
var book = {
    name: "travel notes",
    type: "travel"
}
var travelBook = printBook(book);
travelBook.name
// "travel notes"
travelBook.auther
// "Sue"

上述例子中衡怀,travelBook不繼承了book中的屬性屉来,而且還具有自己的屬性。需要注意的是狈癞,使用寄生式繼承,在第二步中添加的屬性或方法不能在實(shí)例間復(fù)用茂契。

寄生組合式繼承

還記得我們?cè)诮M合繼承中提到的注意事項(xiàng)第一條嗎...為什么Child原型中以及child實(shí)例中都包含givenName屬性蝶桶,這是因?yàn)槲覀冋{(diào)用了兩次Father構(gòu)造函數(shù),第一次將屬性賦予child實(shí)例掉冶,第二次是將Father的實(shí)例賦值給Child的原型真竖,雖然第二次調(diào)用添加的givenName屬性并沒(méi)有影響到child.givenName,但兩次調(diào)用畢竟是冗余的厌小,況且Child只需要繼承Father的原型恢共,而Father的實(shí)例包含了額外的屬性。寄生組合方式就是為了解決上述的問(wèn)題:

function Father(givenName) {
    this.givenName = givenName;
}
Father.prototype.cook = function() {
    return "delicious food";
}
function Child(givenName) {
    Father.call(this, givenName)
}
function inheritPrototype(subType, superType){
    var prototype = object(superType.prototype); 
    prototype.constructor = subType;
    subType.prototype = prototype; 
}
inheritPrototype(Child, Father);
var child = new Child("Sue")
undefined
child.givenName
// "Sue"
"givenName" in Child.prototype
// false
child.cook()
// "delicious food"
child instanceof Father
// true
Father.prototype.isPrototypeOf(child)
// true

Child使用寄生組合的方式繼承了Father璧亚,可以調(diào)用Father原型中的方法讨韭,同時(shí),沒(méi)有留下Father“私有”屬性的痕跡,instanceof()isPrototypeOf()可以正常使用

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末透硝,一起剝皮案震驚了整個(gè)濱河市狰闪,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌濒生,老刑警劉巖埋泵,帶你破解...
    沈念sama閱讀 218,204評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異罪治,居然都是意外死亡丽声,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,091評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)觉义,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)雁社,“玉大人,你說(shuō)我怎么就攤上這事谁撼∑缧玻” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,548評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵厉碟,是天一觀的道長(zhǎng)喊巍。 經(jīng)常有香客問(wèn)我,道長(zhǎng)箍鼓,這世上最難降的妖魔是什么崭参? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,657評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮款咖,結(jié)果婚禮上何暮,老公的妹妹穿的比我還像新娘。我一直安慰自己铐殃,他們只是感情好海洼,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,689評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著富腊,像睡著了一般坏逢。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上赘被,一...
    開(kāi)封第一講書(shū)人閱讀 51,554評(píng)論 1 305
  • 那天是整,我揣著相機(jī)與錄音,去河邊找鬼民假。 笑死浮入,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的羊异。 我是一名探鬼主播事秀,決...
    沈念sama閱讀 40,302評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼彤断,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了秽晚?” 一聲冷哼從身側(cè)響起瓦糟,我...
    開(kāi)封第一講書(shū)人閱讀 39,216評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎赴蝇,沒(méi)想到半個(gè)月后菩浙,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,661評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡句伶,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,851評(píng)論 3 336
  • 正文 我和宋清朗相戀三年劲蜻,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片考余。...
    茶點(diǎn)故事閱讀 39,977評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡先嬉,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出楚堤,到底是詐尸還是另有隱情疫蔓,我是刑警寧澤,帶...
    沈念sama閱讀 35,697評(píng)論 5 347
  • 正文 年R本政府宣布身冬,位于F島的核電站衅胀,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏酥筝。R本人自食惡果不足惜滚躯,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,306評(píng)論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望嘿歌。 院中可真熱鬧掸掏,春花似錦、人聲如沸宙帝。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,898評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)步脓。三九已至息裸,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間沪编,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,019評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工年扩, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留蚁廓,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,138評(píng)論 3 370
  • 正文 我出身青樓厨幻,卻偏偏與公主長(zhǎng)得像相嵌,于是被迫代替她去往敵國(guó)和親腿时。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,927評(píng)論 2 355

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