2019-07-05

繼承

子類的原型對(duì)象--繼承

// 類式繼承
// 聲明父類
function SupperClass(){
    this.superValue = true
}
// 為父類添加公有方法
SupperClass.prototype.getSuperValue = function(){
    return this.superValue;
}
// 聲明類
function SubClass(){
    this.subValue = false;
}
// 繼承父類
SubClass.prototype = new SuperClass()
// 為子類添加公有方法
SubClass.prototype.getValue = function(){
    return this.subValue
}
var instance = new SubClass()
instance.getSuperValue()    // true
instance.getSubValue()      // false

這里聲明類兩個(gè)類, 而且第二個(gè)類的原型prototype被賦予了第一個(gè)類的實(shí)例
類的原型對(duì)象的作用就是為類的原型添加共有方法,但類不能直接訪問這些屬性和方法,必須通過原型prototype來訪問而我們實(shí)例化一個(gè)父類的時(shí)候,新創(chuàng)建的對(duì)象復(fù)制了父類的構(gòu)造函數(shù)內(nèi)的屬性與方法并且將原形proro指向了父類的原型對(duì)象,這樣就擁有了父類的原型對(duì)象上的屬性與方法,并且這個(gè)新創(chuàng)建的對(duì)象可直接訪問到父類原型對(duì)象的屬性與方法.如果我們將這個(gè)新創(chuàng)建的對(duì)象賦值給子類的原型, 那么子類的原型就可以訪問到父類的原型屬性和方法.

我們可以通過instanceof來檢測(cè)某個(gè)對(duì)象是否是某個(gè)類的實(shí)例,或者說某個(gè)對(duì)象是否繼承了某個(gè)類.這樣就可以判斷對(duì)象與類之間的繼承關(guān)系了.
instanceof是通過判斷對(duì)象的prototype鏈來確定這個(gè)對(duì)象是否是某個(gè)類的實(shí)例,而不關(guān)心對(duì)象與類的自身結(jié)構(gòu)

console.log(instance instanceof SuperClass); //true
console.log(instance instanceof SubClass); //true
console.log(SubClass instanceof SuperClass); //false

instanceof 是判斷前面的對(duì)象是否是后面的類(對(duì)象)的實(shí)例, 它并不表示兩者的繼承.
我們?cè)趯?shí)現(xiàn)subClass繼承superClass時(shí)是通過將superClass的實(shí)例賦值給subClass的原型prorotype,所以SubClass.prototype繼承了superClass
console.log(SubClass.prototype instanceof SuperClass) //true
所創(chuàng)建的所有對(duì)象都是Object的實(shí)例
console.log(instance instanceof Object) // true
這種類繼承還有2個(gè)缺點(diǎn).
一: 由于子類通過原型prototype對(duì)父類實(shí)例化,繼承了父類.所以說父類中的共有屬性要是引用類型,就會(huì)在子類中被所有實(shí)例共有, 因此一個(gè)子類的實(shí)例更改子類原型從父類構(gòu)造函數(shù)中繼承的共有屬性就會(huì)影響到其它子類.

function SuperClass(){
    this.books = ['js', 'html', 'css']
}
function SubClass(){}
SubClass.prototype = new SuperClass()
var instance1 = new SubClass();
var instance2 = new SubClass();
console.log(instance2.books); // ['js', 'html', 'css']
instance1.books.push('設(shè)計(jì)模式')
console.log(instance2.books); // ['js', 'html', 'css', '設(shè)計(jì)模式']

instance1 改變了instance2的值
二:由于子類實(shí)現(xiàn)的繼承是靠原型prototype對(duì)父類的實(shí)例化實(shí)現(xiàn)的, 因此在創(chuàng)建父類的時(shí)候,是無法向父類傳遞參數(shù)的, 因?yàn)樵趯?shí)例化父類的時(shí)候也無法對(duì)父類否找函數(shù)內(nèi)的屬性進(jìn)行初始化.

構(gòu)造函數(shù)繼承

// 構(gòu)造函數(shù)式繼承
// 聲明父類
function SuperClass(id){
    // 引用類型共有屬性
    this.books = ['js', 'css', 'html'];
    // 值類型共有屬性
    this.id = id;
}
// 父類聲明原型方法
SupperClass.prototype.showBooks = function(){
    console.log(this.books)
}
// 聲明子類
function subClass(id){
    // 繼承父類
    SuperClass.call(this, id)
}
// 創(chuàng)建第一個(gè)子類的實(shí)例
var instance1 = new subClass(10)
// 創(chuàng)建第二個(gè)子類的實(shí)例
var instance2 = new subClass(1);
instance1.books.push('設(shè)計(jì)模式')
console.log(instance1.books) // ['js', 'css', 'html', '設(shè)計(jì)模式']
console.log(instance1.id) // 10
console.log(instance1.books) // ['js', 'css', 'html']
console.log(instance1.id) // 1
instance1.showBooks() // TypeErroe

SuperClass.call(this, id),call這個(gè)方法可以更改函數(shù)的作用環(huán)境,因此在子類中,對(duì)superClass調(diào)用這個(gè)方法就是將子類匯總的變量在父類中執(zhí)行了一遍,由于父類中是給this綁定屬性的,因此子類自然也就繼承了父類的共有屬性.由于這種類型的繼承沒有涉及原型prototype,所以父類的原型方法自然不會(huì)倍子類繼承, 而如果想要被子類繼承就必須放在構(gòu)造函數(shù)中,這樣創(chuàng)建出的每個(gè)實(shí)例都會(huì)單獨(dú)擁有一份而不是共用,這樣就違背了代碼復(fù)用的原則.綜合這兩種模式的優(yōu)點(diǎn),后來就有了組合繼承

組合繼承

類式繼承是用過子類的原型prototype對(duì)父類實(shí)例化來實(shí)現(xiàn)的,構(gòu)造函數(shù)式繼承是通過在子類的構(gòu)造函數(shù)作用環(huán)境中執(zhí)行一次父類的構(gòu)造函數(shù)來實(shí)現(xiàn)的,所以只要在繼承中同時(shí)做到這兩點(diǎn)即可.

// 組合式繼承
// 聲明父類
function SuperClass(name){
    // 值類型共有屬性
    this.name = name;
    // 應(yīng)用類型共有屬性
    this.books = ['js', 'css', 'html'];
}
// 父類原型共有方法
SuperClass.prototype.getName = function(){console.log(this.name)}
// 聲明子類
function SubClass(name, time){
    // 構(gòu)造函數(shù)式繼承父類name屬性
    SuperClass.call(this, name)
    // 子類中新增共有屬性
    this.time = time;
}
// 類式繼承 子類原型繼承父類
SubClass.prototype = new SupperClass();
// 子類原型方法
SubClass.prototype.getTime = function(){console.log(this.time)}

在子類構(gòu)造函數(shù)中執(zhí)行父類構(gòu)造函數(shù),在子類原型上實(shí)例化父類就是組合模式,這樣就融合了類式繼承的優(yōu)點(diǎn)

var instance1 = new SubClass('js book', 2014)
instance1.books.push('設(shè)計(jì)模式')
console.log(instance1.books); // ['js', 'css', 'html', '設(shè)計(jì)模式']
instance1.getName() // js book
instance1.getTiem() // 2014
var instance2 = new SubClass('css book', 2011);
console.log(instance2.books) // ['js', 'css', 'html']
instance2.getName() // css book
instance2.getTime() // 2011

這種繼承模式在使用構(gòu)造函數(shù)繼承時(shí)執(zhí)行了一遍父類的構(gòu)造函數(shù), 而在實(shí)現(xiàn)子類原型的類式繼承時(shí)又調(diào)用了一遍父類構(gòu)造函數(shù).因此父類構(gòu)造函數(shù)調(diào)用了兩遍.

潔凈的繼承者--原型式繼承

借助原型prototype可以根據(jù)已有的對(duì)象創(chuàng)建一個(gè)新的對(duì)象, 同時(shí)不必創(chuàng)建新的自定義對(duì)象類型

// 原型是繼承
function inheritObject(o){
    // 聲明一個(gè)過渡函數(shù)對(duì)象
    function F () {}
    // 過渡對(duì)象的原型繼承父對(duì)象
    F.prototype = o;
    // 返回過度對(duì)象的一個(gè)實(shí)例, 該實(shí)例的原型繼承了父對(duì)象
    return new F();
}

它是對(duì)類式繼承的一個(gè)封裝,過渡對(duì)象就相當(dāng)于類式繼承中的子類,只不過在原型中作為一個(gè)過渡對(duì)象出現(xiàn)的, 目的是為了創(chuàng)建要返回的新的實(shí)例化對(duì)象,由于F過渡類的構(gòu)造函數(shù)中無內(nèi)容, 開銷比較小, 使用起來也比較方便.

var book = {
    name: 'js book';
    alikeBook: ['css', 'js']
}
var newBook = inheritObject(book);
newBook.name = 'ajax book';
newBook.alikeBook.push('as book')
var otherBook = inheritObject(book);
newBook.name = 'flash book';
newBook.alikeBook.push('xml book')
console.log(newBook.name); // ajax book
console.log(newBook.alikeBook); // ['css'. 'js', 'as book', 'xml book']
console.log(otherBook.name); // flash book
console.log(otherBook.alikeBook); // ['css'. 'js', 'as book', 'xml book']
console.log(book.name); // js book
console.log(book.alikeBook); // ['css'. 'js', 'as book', 'xml book']

寄生式繼承

function inheritObject(o){
    function F(){}
    F.prototype = o;
    return new F();
}
// 寄生式繼承
// 聲明基對(duì)象
var book = {
    name: "js book",
    alikeBook: ["css", "js", "html"]
};
function createBook(obj){
    // 通過原型繼承方式創(chuàng)建新對(duì)象
    var o = new inheritObject(obj)
    o.getName = function(){
        console.log(name)
    };
    // 返回拓展后的新對(duì)象
    return o;
}

寄生式繼承就是對(duì)原型繼承的第二次封裝,并且在這第二次封裝中對(duì)繼承的對(duì)象進(jìn)行拓展券勺,這樣創(chuàng)建的對(duì)象不僅僅有父類中的屬性和方法而且還添加新的屬性和方法
寄生大概值得就是像寄生蟲一樣寄托于某個(gè)對(duì)象內(nèi)部生長(zhǎng)绪钥。當(dāng)然寄生式繼承這種增強(qiáng)新創(chuàng)建對(duì)象的繼承思想也是寄托于原型繼承模式

終極繼承者--寄生組合式繼承

組合式繼承將類式繼承同構(gòu)造函數(shù)繼承組合使用,但是這種方式有一個(gè)問題关炼,就是子類不是父類的實(shí)例程腹,而子類的原型是父類的實(shí)例,所以才有了寄生組合式繼承
寄生式寄生式繼承儒拂, 寄生式繼承依托于原型繼承寸潦, 原型繼承又與類繼承相像色鸳,另一種繼承模式式構(gòu)造函數(shù)繼承, 子類不是父類實(shí)例的問題是由于類是繼承引起的

/**
* 寄生式繼承 類繼承
* 傳遞參數(shù) subClass 子類
* 傳遞參數(shù) superClass 父類
**/
function inheritObject(o){
    var o = new F();
    F.prototype = o;
    return new F();
}
function inheritPrototype(subClass, superClass){
    // 復(fù)制一份父類的原型副本保存在變量中
    var p = inheritObject(superClass.prototype);
    // 修正因?yàn)橹貙懽宇愒蛯?dǎo)致子類的constructor屬性被修改
    p.constructor = subClass;
    // 設(shè)置子類的原型
    subClass.prototype = p;
}

組合式繼承中见转,通過構(gòu)造函數(shù)繼承的屬性和方法是沒問題的命雀, 所以這里我們組要理解通過寄生式繼承重新繼承父類的原型。我們需要繼承的僅僅是父類的原型池户,不再需要調(diào)用父類的構(gòu)造函數(shù)咏雌,換句話說,在構(gòu)造函數(shù)繼承中我們已經(jīng)調(diào)用了父類的構(gòu)造函數(shù)校焦,因此我們需要的就是父類的原型對(duì)象的一個(gè)副本赊抖,而這個(gè)副本我們通過原型繼承便可得到,但是這么直接賦值給子類會(huì)有問題寨典,因?yàn)楦割愒蛯?duì)象復(fù)制得到的復(fù)制對(duì)象p中的constructor指向的不是subClass子類對(duì)象氛雪,因此寄生式繼承中要對(duì)復(fù)制對(duì)象p做一次增強(qiáng),修復(fù)其constructor屬性指向不正確的問題耸成,最后將得到的復(fù)制對(duì)象p復(fù)制給子類的原型报亩,這樣子類的原型就繼承了父類的原型并且沒有執(zhí)行父類的構(gòu)造函數(shù)

// 定義父類
function SuperClass(name){
    this.name = name;
    this.colors = ["red", "blue", "green"];
}
// 定義父類原型方法
SuperClass.prototype.getName = function (){
    console.log(this.name);
};
// 定義子類
function SubClass(name, time){
    // 構(gòu)造函數(shù)式繼承
    SuperClass.call(this, name)
    // 子類新增屬性
    this.time = time;
}
// 寄生式繼承父類原型
inheritPrototype(SubClass, SuperClass);
// 子類新增原型方法
SubClass.prototype.getTime = function(){
    console.log(this.time);
};
// 創(chuàng)建兩個(gè)測(cè)試方法
var instance1 = new SubClass("js book", 2014)
var instance2 = new SubClass("css book", 2015)
console.log(instance1.colors); // ["red", "blue", "green"];
console.log(instance2.colors); // ["red", "blue", "green"];
instance2.getName(); // 'css book'
instance2.getTime(); // '2015'

多繼承

在JS中繼承是依賴于原型prototype鏈實(shí)現(xiàn)的, 只有一條原型鏈井氢, 所以理論不能繼承多個(gè)父類

extend

// 單繼承 屬性賦值
var extend = function(target, source){
    // 遍歷源對(duì)象的屬性
    for(var property in source){
        // 將源對(duì)象中的屬性復(fù)制到目標(biāo)對(duì)象中
        targer[property] = source[property]
    }
    // 返回目標(biāo)對(duì)象
    return target;
}
var book = {
    name: 'js',
    alike: ['css', 'js', 'html']
}
var anotherBook = {
    color: 'red'
}
extend(anotherBook, book)
console.log(anotherBook.name) // js
console.log(anotherBook.alike) // ['css', 'js', 'html']
anotherBook.alike.push('ajax');
anotherBook.name = '設(shè)計(jì)模式';
console.log(anotherBook.name); //設(shè)計(jì)模式
console.log(anotherBook.alike); // ['css', 'js', 'html', 'ajax']
console.log(book.name); // js
console.log(book.alike); // ['css', 'js', 'html', 'ajax']

多繼承

// 多繼承 屬性復(fù)制
var mix = fuction(){
    var i = 1,
        len = arguments.length,
        target = arguments[0],
        arg;
    // 遍歷被繼承的對(duì)象
    for (; i < len; i++){
        // 緩存當(dāng)前對(duì)象
        arg = argument[i];
        // 遍歷被緩存的對(duì)象中的屬性
        for (var property in arg){
            // 將被緩存對(duì)象中的屬性復(fù)制到新目標(biāo)中
            target[property] = arg[property]
        }
    }
    return target;
}

綁定到Object上

Object.prototype.mix = function(){
    var i = 0,
        len = arguments.length,
        arg;
    // 遍歷被繼承的對(duì)象
    for (; i < len; i++){
        // 緩存當(dāng)前對(duì)象
        arg = argument[i];
        // 遍歷被緩存的對(duì)象中的屬性
        for (var property in arg){
            // 將被緩存對(duì)象中的屬性復(fù)制到新目標(biāo)中
            this[property] = arg[property]
        }
    }
}
otherBook.mix(book1, book2);

多態(tài)

多態(tài)是同一個(gè)方法多用調(diào)用方式

// 多態(tài) 
// 定義一個(gè)方法弦追, 如果不傳參數(shù)返回10, 如果傳一個(gè)參數(shù)返回10+參數(shù) 如果傳兩個(gè)參數(shù)就返回兩個(gè)參數(shù)相加的結(jié)果
function add(){
    // 獲取參數(shù)
    var arg = arguments,
        len = arg.length;
    switch(len){
        case 0:
            return 10;
        case 1:
            return 10 + arg[0];
        case 2:
            return arg[0] + arg[1];
    }
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末花竞,一起剝皮案震驚了整個(gè)濱河市劲件,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌约急,老刑警劉巖零远,帶你破解...
    沈念sama閱讀 218,640評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異厌蔽,居然都是意外死亡牵辣,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,254評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門奴饮,熙熙樓的掌柜王于貴愁眉苦臉地迎上來纬向,“玉大人,你說我怎么就攤上這事戴卜“罩恚” “怎么了?”我有些...
    開封第一講書人閱讀 165,011評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵叉瘩,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我粘捎,道長(zhǎng)薇缅,這世上最難降的妖魔是什么危彩? 我笑而不...
    開封第一講書人閱讀 58,755評(píng)論 1 294
  • 正文 為了忘掉前任,我火速辦了婚禮泳桦,結(jié)果婚禮上汤徽,老公的妹妹穿的比我還像新娘。我一直安慰自己灸撰,他們只是感情好谒府,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,774評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著浮毯,像睡著了一般完疫。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上债蓝,一...
    開封第一講書人閱讀 51,610評(píng)論 1 305
  • 那天壳鹤,我揣著相機(jī)與錄音,去河邊找鬼饰迹。 笑死芳誓,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的啊鸭。 我是一名探鬼主播锹淌,決...
    沈念sama閱讀 40,352評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼赠制!你這毒婦竟也來了赂摆?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,257評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤憎妙,失蹤者是張志新(化名)和其女友劉穎库正,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體厘唾,經(jīng)...
    沈念sama閱讀 45,717評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡褥符,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,894評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了抚垃。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片喷楣。...
    茶點(diǎn)故事閱讀 40,021評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖鹤树,靈堂內(nèi)的尸體忽然破棺而出铣焊,到底是詐尸還是另有隱情,我是刑警寧澤罕伯,帶...
    沈念sama閱讀 35,735評(píng)論 5 346
  • 正文 年R本政府宣布曲伊,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏坟募。R本人自食惡果不足惜岛蚤,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,354評(píng)論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望懈糯。 院中可真熱鬧涤妒,春花似錦、人聲如沸赚哗。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,936評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽屿储。三九已至贿讹,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間扩所,已是汗流浹背围详。 一陣腳步聲響...
    開封第一講書人閱讀 33,054評(píng)論 1 270
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留祖屏,地道東北人助赞。 一個(gè)月前我還...
    沈念sama閱讀 48,224評(píng)論 3 371
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像袁勺,于是被迫代替她去往敵國(guó)和親雹食。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,974評(píng)論 2 355

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