Javascript —— 繼承

本文是本人學習,張容銘著的《Javascript 設計模式》的筆記與總結。僅供學習交流。

每個類都有3個部分:

1、第一部分是構造函數內的,這是供實例化對象復制用的。

2、第二部分是構造函數外的,直接通過點語法添加,這是供類使用的,實例化對象是訪問不到的。

3、第三部分是類的原型中的,實例化對象是可以通過其原型鏈間接地訪問到,也是為供所有實例化對象所共用的。

一、子類的原型對象————類式繼承

類式繼承:通過子類的原型prototype對父類實例化來實現。

//類式繼承
//聲明父類
function SuperClass(){
    this.superValue = true;
}
//為父類添加共有方法
SuperClass.prototype.getSuperValue = function () {
    return this.superValue;
};

// 聲明子類
function SubClass(){
    this.subValue = false;
}

// 繼承父類
SubClass.prototype = new SuperClass ();

// 為子類添加共有方法
SubClass.prototype.getSubValue = function (){
    return this.subValue;
}

上面聲明了2個類,而且第二個類的原型prototype 被賦予了第一個類的實例。
類的原型對象的作用就是為類的原型添加共有方法,但類不能直接訪問這些屬性個方法,必須通過原型prototype來訪問。 


var instance = new SubClass();
console.log(instance.getSuperValue() ); // true
console.log(instance.getSubValue()); //false

通過instanceof 來檢測某個對象是否是某個類的實例,或者說某個對象是否繼承了某個類。

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

第三個false,說明 ,instanceof 是判斷前面的對象是否是后面類(對象)的實例,它并不表示兩者的繼承。

>> SubClass 繼承 SuperClass 時是通過將superClass的實例賦予給SubClass 的原型prototype,所以說 SubClass.prototype繼承了SuperClass

console.log(SubClass.prototype instanceof SuperClass); //true

>> Object是所有對象的祖先
console.log(instance instanceof Object);    //true

類式繼承的缺點

1、一個子類的實例更改子類原型從父類構造函數中繼承來的共有屬性就會直接影響到其他子類。原因:
由于子類通過其原型prototype對父類實例化,繼承了父類。所以說父類中的共有屬性要是引用類型,就會在子類中被所有實例共用。

示例代碼:

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

2、在實例化父類的時候也無法對父類構造函數內的屬性進行初始化。原因:

由于子類實現的繼承是靠其原型prototype對父類的實例化實現的,因此在創建父類的時候,是無法向父類傳遞參數的。

二、創建即繼承——構造函數繼承

構造函數式:通過在子類的構造函數環境中執行一次父類的構造函數來實現的。用call(this,變量);

//構造函數式繼承
//聲明父類

function SuperClass (id){
    //引用類型共有屬性
    this.books = ['JavaScript','html','css'];
    //值類型共有屬性
    this.id = id;
}
//父類聲明原型方法
SuperClass.prototype.showBooks = function (){
    console.log(this.books);
}
//聲明子類
function SubClass (id) {
    //繼承父類
    SuperClass.call (this, id);
}

//創建第一個子類的實例
var instance1 = new SubClass (10);
var instance2 = new SubClass (11);

instance1.books.push('設計模式');
console.log(instance1.books);   // ['JavaScript','html','css','設計模式'];
console.log(instance1.id);  //10
console.log(instance2.books);   //['JavaScript','html','css'];
console.log(instance2.id);  //11

instance1.showBooks (); //TypeError

構造函數式繼承的精華:
SuperClass.call (this, id);

優缺點:

call將子類的變量在父類中執行一遍,由于父類是給this綁定屬性的,所以子類自然也就繼承了父類的共有屬性。而這種類型的繼承沒有涉及原型prototype,所以父的原型方法自然不會被子類繼承。如果想被子類繼承就必須要放在構造函數中。

三、組合繼承 (類式+ 構造式)

組合繼承結合了類式繼承 + 構造函數繼承

1、類式繼承:通過子類的原型prototype對父類實例化來實現。

2、構造函數式:通過在子類的構造函數環境中執行一次父類的構造函數來實現的。用call(this,變量);

//組合式繼承
//聲明父類
function SuperClass(name){
    //值類型共有屬性
    this.name = name;
    //引用類型共有屬性
    this.books = ['html'、'css'、'Javascript'];
}

// 父類原型共有方法

SuperClass.prototype.getName = function () {
    console.log(this.name);
};
//聲明子類
function SubClass (name, time) {
    //構造函數式繼承父類name屬性
    SuperClass.call(this.name);
    //子類中新增共有屬性
    this.time = time;
}

//類式繼承 子類原型繼承父類
SubClass.prototype = new SuperClass();
//子類原型方法
SubClass.prototype.getTime = function (){
    console.log(this.time);
}

如上,在子類構造函數中執行父類構造函數,在子類原型上實例化父類就是組合模式。這樣融合了類式繼承和構造函數繼承的優點,并且過濾掉其缺點,你測試看看。

測試代碼:

var instance1 = new SubClass("js book", 2014);
instance1.book.push("設計");
console.log(instance1.books);   //["html","css","Javascript","設計模式"]
instance1.getName ();   //js book
instance1.getTime ();   // 2014

var instance2 = new SubClass ("css book", 2013);
console.log(instance2.books);   //["html","css","Javascript"]
instance2.getName ();   //css book
instance2.getTime ();   //2013

優缺點

子類的實例中更改父類繼承下來的引用類型屬性如books,不會影響到其他實例,并且子類實例化過程中又能將參數傳遞到父類的構造函數中。

四、原型式繼承

//原型式繼承
function inheritObject (o){
    //聲明一個過渡函數對象
    function F (){ }
    //過渡對象的原型繼承父對象
    F.prototype = o;
    //返回過渡對象的一個實例,該實例的原型繼承了父對象
    return new F();
}

這是類式繼承的一個封裝

測試代碼:

var book = {
    name : "js book",
    alikeBook : ["css book","html book"]
}

var newBook = inheritObject (book);
newBook.name = "ajax book";
newBook.alikeBook.push("xml book");

var otherBook = inheritObject (book);
otherBook.name = "flash book";
otherBook.alikeBook.push("as book");

console.log(newBook.name);  //ajax book

console.log(newBook.alikeBook); //["css book","html book","xml book","as book"]

console.log(otherBook.name);    //flash book

console.log(otherBook.alikeBook);   //["css book","html book","xml book","as book"]

console.log(book.name);     // js book

console.log(book.alikeBook);    //["css book","html book","xml book","as book"]

優缺點

跟類式繼承一樣,父類對象book中的值類型的屬性被復制,引用類型的屬性被共同用

五、寄生式繼承

// 寄生式繼承
// 聲明基對象
var book = {
    name : "js book",
    alikeBook : ["css book", "html book"]
}

function createBook (obj){
    //通過原型繼承方式創建新對象
    var o = new inheritObject(obj);
    //拓展新對象
    o.getName = function (){
        console.log(name);
    };
    //返回拓展后的新對象
    return o;
}

優缺點

寄生式繼承是對原型的第二次封裝,并且在這第二次封裝過程中對繼承的對象進行了拓展,這樣新創建的對象不僅僅有父類中的屬性和方法而且還添加新的屬性和方法。

六、寄生組合式繼承

/**
* 寄生式繼承 繼承原型
* 傳遞參數 subClass 子類
* 傳遞參數 superClass 父類
**/
function inheritPrototype(subClass, superClass){
    //復制一份父類的原型副本保存在變量中
    var p = inheritObject (superClass.prototype);
    //修正因為重寫子類原型導致子類的constructor 屬性被修改;
    p.constructor = subClass;
    //設置子類的原型
    subClass.prototype = p;
}

測試用例

// 定義父類
function SuperClass (name){
    this.name = name;
    this.colors = ["red","blue","green"];
}
// 定義父類原型方法
SubClass.prototype.getName = function () {
    console.log(this.name);
}
// 定義子類
function SubClass (name, time){
    // 構造函數式繼承
    SuperClass.call (this, name);
    // 子類新增屬性
    this.time = time;
}

//寄生式繼承父類原型
inheriPrototype(SubClass, SuperClass);
//子類新增原型方法
SubClass.prototype.getTime = function (){
    console.log(this.time);
};
// 創建兩個測試方法
var instance1 = new SubClass("js book", 2014);
var instance2 = new SubClass("css book", 2013);

測試代碼

instance1.colors.push("black");
console.log(instance1.colors);  //["red","blue","green","black"]
console.log(instance2.colors);  //["red","blue","green"]
instance2.getName ();   //css book
instance2.getTime ();   //2013

七、多繼承

一些面向對象語言中支持多繼承,在javascript中也能實現,但是有些局限性。只有一條原型鏈,理論上不能繼承多個父類。而通過 extend 方法可以做到。

// 單繼承 屬性復制

var extend = function (target, source){
    //遍歷源對象中的屬性
    for (var property in source){
        //將源對象中的屬性復制到目標對象中
        target[property] = source[property];
    }
    //返回目標對象
    return target;
}

extend方法是一個淺復制的過程,他只能復制值類型的屬性,對于引用類型的屬性是不行的。

單繼承測試代碼

var book =  {
    name : 'javascript 設計模式',
    alike : ['css','html','javascript']
}
var anotherBook = {
    color : 'blue'
}
extend(anotherBook, book);
console.log(anotherBook.name);  //Javascript 設計模式
console.log(anotherBook.alike);     // ['css','html','javascript']

anotherBook.alike.push('ajax');
anotherBook.name = '設計模式';
console.log(anotherBook.name);  // 設計模式
console.log(anotherBook.alike);     //['css','html','javascript','ajax']
console.log(book.name);   //Javascript 設計模式
console.log(book.alike);   // ['css','html','javascript']

多繼承

//多繼承 屬性復制
var mix = function (){
    var i = 1,  // 從第二個參數起為被繼承的對象
        len = arguments.length, //獲取參數長度
        target = arguments[0],  ///第一個對象為目標對象
        arg;    //緩存參數對象
    // 遍歷被繼承對象
    for(; i<len; i++){
        // 緩存當前對象
        arg = arguments[i];
        //遍歷被繼承對象中的屬性
        for (var property in arg){
            //將被繼承對象中的屬性復制到目標對象中
            target[property] =arg[property];
        }
    }
    // 返回目標對象
    return target;
}

//將mix綁定到原生對象Object上,所有的對象都可以擁有這個方法。
Object.prototype.mix = function (){
    var i = 0,  //從第一個參數起為被繼承的對象
        len = arguments.length, // 獲取參數長度
        arg;
    //遍歷被繼承的對象
    for(; i<len; i++){
        // 緩存當前對象
        arg = arguments[i];
        //遍歷被繼承對象中的屬性
        for (var property in arg){
            //將被繼承對象中的屬性復制到目標對象中
            this[property] = arg[property];
        }
    }
}

// 調用
var book1 = {name:'書一',publish:'廣州'};
var book2 = {cover:'http://www.baidu.com',title:'sdfdf'};
var otherBook = {content:'df'};
otherBook.mix(book1,book2);
console.log(JSON.stringify(otherBook)); //{"content":"df","name":"書一","publish":"廣州","cover":"http://www.baidu.com","title":"sdfdf"}

八、多種調用方式————多態

//場景: 一個參數返回平方, 兩個參數相乘,三個參數相加再相乘,三個以上相加
// 多態
function total () {
    // 獲取參數
    var arg = arguments,
        //獲取參數長度
        len = arg.length;
        switch(len){
            //如果沒有參數
            case 0:
                return 0;
            case 1:
                return Math.pow(10,arg[0]);
            case 2:
                return arg[0] * arg[1];
            case 3:
                return (arg[0] + arg[1])*arg[3];
            defult:
                var a=0;
                for(var i=0; i<len ;i++){
                    a += arg[i];
                }
                return a;
        }
    
}

根據不同數量的參數有不同的處理

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,885評論 6 541
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,312評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,993評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,667評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,410評論 6 411
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,778評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,775評論 3 446
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,955評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,521評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,266評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,468評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,998評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,696評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,095評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,385評論 1 294
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,193評論 3 398
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,431評論 2 378

推薦閱讀更多精彩內容

  • 我是誰,我來自哪,我是誰的誰 想必大家一定在學習或者開發過程常常被JS獨有的原型繼承撥過不少腦弦吧,為何不迎問題而...
    俗三瘋閱讀 326評論 0 2
  • JavaScript有多種繼承模式,總結起來用到的方法有:原型鏈的傳遞、構造函數的借用、對象的復制。 對于原型鏈和...
    minxuan閱讀 6,969評論 5 29
  • 例子 我們生成兩個構造函數,后面的例子都是讓‘’貓‘’繼承‘’動物‘’的所有屬性和方法。 動物(為了更好的理解各種...
    流光號船長閱讀 336評論 0 1
  • 之前的JavaScript繼承一文中已經介紹了繼承,但那篇只能算簡介。本篇結合原型鏈詳細介紹一下JavaScrip...
    張歆琳閱讀 2,606評論 0 8
  • 稍有所想不得不敲打紀錄下來,想到工作,想到生活,想到友誼,想到了我還有用。更是想到現在如重生般的自己! ...
    賢之閱讀 307評論 0 0