首頁 web前端 js教程 關於JS繼承的詳解

關於JS繼承的詳解

Mar 13, 2018 pm 05:31 PM
javascript 繼承 詳解

這次帶給大家關於JS繼承的詳解,使用JS繼承的注意事項有哪些,下面就是實戰案例,一起來看一下。

ECMAScript 實作繼承的方式不只一種。這是因為 JavaScript 中的繼承機制並不是明確規定的,而是透過模仿來實現的。這意味著所有的繼承細節並非完全由解釋程序處理。可依需求決定適合的繼承方式。

物件冒充

建構子使用this關鍵字給所有屬性與方法賦值(即採用類別宣告的建構子方式)。因為建構函數只是一個函數,所以可讓ClassA建構函數成為ClassB的方法,然後呼叫它。 ClassB就會收到ClassA的建構函式中定義的性質和方法。

function ClassA(name) {    this.name = name;    this.sayName = function () {        console.log(this.name);
    };
}function ClassB(name,age) {    this.classA = ClassA;    this.classA(name);    delete this.classA;    this.age = age;    this.sayAge = function(){        console.log(this.age);
    }
}var tom = new ClassA('Tom');var jerry = new ClassB('Jerry',25);
tom.sayName();                         //'Tom'jerry.sayName();                       //'Jerry'jerry.sayAge();                        //25console.log(tom instanceof ClassA);    //trueconsole.log(jerry instanceof ClassA);  //falseconsole.log(jerry instanceof ClassB);  //true
登入後複製

所有新屬性和新方法都必須在刪除了新方法的程式碼行後定義,因為可能會覆寫超類別的相關屬性和方法

物件冒充可以實現多重繼承
如果存在ClassA和ClassB,這時ClassC想繼承這兩個類,如下:

function ClassA(name){    this.name = name;    this.sayName = function (){        console.log(this.name);
    }
}function ClassB(age){    this.age = age;    this.sayAge = function(){        console.log(this.age);
    }
}function ClassC(name,age){    this.method = ClassA;    this.method(name);    
    this.method = ClassB;    this.method(age);    delete this.method;
}var tom = new ClassC('Tom',25);
tom.sayName();                       //'Tom';tom.sayAge();                        //25console.log(tom instanceof ClassA);  //falseconsole.log(tom instanceof ClassB);  //falseconsole.log(tom instanceof ClassC);  //true
登入後複製

這種實作方式的缺陷是:如果兩個類ClassA和ClassB有同名的屬性或方法,ClassB具有高優先級,因為它從後面的類別繼承。
由於這種繼承方法的流行,ECMAScript的第三版為Function物件加入了兩個方法,即call()和apply()。

call方法是與經典的物件冒充方法最相似的方法。它的第一個參數用作this的物件,其他參數都直接傳遞給函數本身

function sayName(prefix) {    console.log(prefix + this.name);
};var tom = {};
tom.name = "Tom";
sayName.call(tom, 'This is ');  //'This is Tom'
登入後複製

函數sayName在物件外定義,但也可以引用this。
call方法改寫物件冒充

function ClassA(name){    this.name = name;    this.sayName = function(){        console.log(this.name);    }}function ClassB(name,age){    //this.method = ClassA;    //this.method(name);    //delete this.method;    ClassA.call(this,name);    this.age = age;    this.sayAge = function (){        console.log(this.age);    }}var tom = new ClassB('Tom',25);tom.sayName();                       //'Tom'tom.sayAge();                        //25console.log(tom instanceof ClassA);  //falseconsole.log(tom instanceof ClassB);  //true
登入後複製

call方法取代了使用屬性引用ClassA的方式。

apply

apply方法有兩個參數,用作this的物件和要傳遞給函數的參數陣列

function sayName(prefex,mark) {    console.log(prefex+ this.name+ mark);
};var tom = {};
tom.name = 'Tom';
sayName.apply(tom, ['This is ','!']);  //'This is Tom!'
登入後複製

同樣可以使用apply改寫物件冒充

function ClassA(name){    this.name = name;    this.sayName = function(){        console.log(this.name);    }}function ClassB(name,age){    ClassA.apply(this,arguments);    this.age = age;    this.sayAge = function (){        console.log(this.age);    }}var tom = new ClassB('Tom',25);tom.sayName();                       //'Tom'tom.sayAge();                        //25  console.log(tom instanceof ClassA);  //falseconsole.log(tom instanceof ClassB);  //true
登入後複製

只有超類別中參數順序和子類別中的參數完全一致時才可以傳遞參數陣列

原型鏈

# prototype物件是個模板,要實例化的物件都以這個模板為基礎,prototype物件的任何屬性和方法都會傳遞給這個類別的所有實例,原型鏈就是利用這種功能來實現繼承機制。

function ClassA() {}ClassA.prototype.name = 'Tom';ClassA.prototype.sayName = function () {    console.log(this.name);};function ClassB() {}ClassB.prototype = new ClassA();var tom = new ClassB();tom.sayName();                       //'Tom'console.log(tom instanceof ClassA);  //trueconsole.log(tom instanceof ClassB);  //true
登入後複製

這裡把ClassB的prototype屬性設定稱ClassA的實例,避免逐一賦值prototpye屬性。
在呼叫ClassA時沒有設定參數,因為在原型鏈中要確保建構子是無參的。
在原型鏈中,instanceof的結果也有了變化,對於ClassA和ClassB都回傳了true。

因為prototype屬性的重指定,子類別中的新屬性都必須出現在prototype被賦值後。

function ClassA() {}ClassA.prototype.name = 'Tom';ClassA.prototype.sayName = function () {    console.log(this.name);};function ClassB() {}ClassB.prototype = new ClassA();ClassB.prototype.age = 25;ClassB.prototype.sayAge = function () {    console.log(this.age);};var tom = new ClassA();var jerry = new ClassB();tom.sayName();                         //'Tom'jerry.sayName();                       //'Tom'jerry.name = 'Jerry';tom.sayName();                         //'Tom'jerry.sayName();                       //'Jerry'jerry.sayAge();                        //25console.log(tom instanceof ClassA);    //trueconsole.log(jerry instanceof ClassA);  //trueconsole.log(jerry instanceof ClassB);  //true
登入後複製

原型鏈的缺陷是不能實現多重繼承,因為類別的prototype會被重寫。

混合方式

物件冒充的問題是必須使用建構函數方式,而使用原型鏈就無法使用帶參數的建構函數,不過,可以試試兩者結合。
用物件冒充繼承建構函式的屬性,用原型鏈繼承prototype的方法。

function ClassA(name) {    this.name = name;
}
ClassA.prototype.sayName = function () {    console.log(this.name);
};function ClassB(name, age) {
    ClassA.call(this, name);    this.age = age;
}
ClassB.prototype = new ClassA();
ClassB.prototype.sayAge = function () {    console.log(this.age);
};var tom = new ClassA('Tom');var jerry = new ClassB('Jerry',25);console.log(tom instanceof ClassA);                    //trueconsole.log(jerry instanceof ClassA);                  //trueconsole.log(jerry instanceof ClassB);                  //trueconsole.log(jerry.constructor === ClassA);             //trueconsole.log(ClassB.prototype.constructor === ClassA);  //true
登入後複製

在ClassB建構子中用物件冒充繼承了ClassA的name屬性,用原型鏈繼承了ClassA的sayName方法,由於使用了原型鏈繼承方式,instanceof運作方式正常。
但是constructor屬性揭露出了問題,每一個prototype物件都有一個constructor屬性指向它的建構函數,而ClassB實例的建構子卻指向了ClassA,這會導致繼承鏈的錯亂。可以手動修改constructor的指向。

function ClassA(name) {    this.name = name;
}
ClassA.prototype.sayName = function () {    console.log(this.name);
};function ClassB(name, age) {
    ClassA.call(this, name);    this.age = age;
}
ClassB.prototype = new ClassA();
ClassB.prototype.constructor = ClassB;
ClassB.prototype.sayAge = function () {    console.log(this.age);
};var tom = new ClassA('Tom');var jerry = new ClassB('Jerry',25);console.log(tom instanceof ClassA);                    //trueconsole.log(jerry instanceof ClassA);                  //trueconsole.log(jerry instanceof ClassB);                  //trueconsole.log(ClassA.constructor === ClassB);            //falseconsole.log(jerry.constructor === ClassA);             //falseconsole.log(ClassB.prototype.constructor === ClassA);  //false
登入後複製

直接繼承原型鏈

為了節省內存,可以不執行創建ClassA實例,直接讓ClassB的原型指向ClassA的原型

function ClassA(name) {    this.name = name;
}
ClassA.prototype.sayName = function () {    console.log(this.name);
};function ClassB(name, age) {
    ClassA.call(this, name);    this.age = age;
}
ClassB.prototype = ClassA.prototype;
ClassB.prototype.constructor = ClassB;
ClassB.prototype.sayAge = function () {    console.log(this.age);
};var tom = new ClassA('Tom');var jerry = new ClassB('Jerry',25);console.log(ClassA.prototype.hasOwnProperty('sayAge'));  //trueconsole.log(ClassA.prototype.constructor === ClassB);   //true
登入後複製

這樣的缺陷是由於直接修改原型鏈指向,對於ClassB原型鏈中的性質也會影響到ClassA上,於是就出現了ClassA具有sayAge方法、ClassA的建構子性質為ClassB。

空物件作中介

為解決直接繼承原型鏈的缺點,可以利用一個空物件作為中介。

function ClassA(name) {    this.name = name;
}
ClassA.prototype.sayName = function () {    console.log(this.name);
};function ClassB(name, age) {
    ClassA.call(this, name);    this.age = age;
}var fn = function(){};
fn.prototype = ClassA.prototype;
ClassB.prototype = new fn();
ClassB.prototype.constructor = ClassB;
ClassB.prototype.sayAge = function () {    console.log(this.age);
};console.log(ClassA.prototype.hasOwnProperty('sayAge'));  //falseconsole.log(ClassA.prototype.constructor === ClassB);    //false
登入後複製

雖然還是建立了物件實例,但由於空物件幾乎不佔內存,修改ClassB的原型也不會影響到ClassA。

封裝成extends方法

function extends(child,parent){    var fn = function (){};
    fn.prototype = parent.prototype;
    child.prototype = new fn();
    child.prototype.constructor = child;
    child.super = parent.prototype;
}
登入後複製

JS的靈活性使我們可以透過多種方式實現繼承,了解其中的原理和實作可以幫助我們在不同的場景中選擇適合的方法。

相信看了本文案例你已經掌握了方法,更多精彩請關注php中文網其它相關文章!

推薦閱讀:

用JS程式碼做出彈幕效果

用H5的canvas做出彈幕效果

#

以上是關於JS繼承的詳解的詳細內容。更多資訊請關注PHP中文網其他相關文章!

本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

C++ 函式繼承詳解:如何在繼承中使用「基底類別指標」和「衍生類別指標」? C++ 函式繼承詳解:如何在繼承中使用「基底類別指標」和「衍生類別指標」? May 01, 2024 pm 10:27 PM

在函數繼承中,使用「基底類別指標」和「衍生類別指標」來理解繼承機制:基底類別指標指向派生類別物件時,執行向上轉型,只存取基底類別成員。派生類別指標指向基底類別物件時,執行向下轉型(不安全),必須謹慎使用。

Win11管理員權限取得詳解 Win11管理員權限取得詳解 Mar 08, 2024 pm 03:06 PM

Windows作業系統是全球最受歡迎的作業系統之一,其新版本Win11備受矚目。在Win11系統中,管理員權限的取得是一個重要的操作,管理員權限可以讓使用者對系統進行更多的操作和設定。本文將詳細介紹在Win11系統中如何取得管理員權限,以及如何有效地管理權限。在Win11系統中,管理員權限分為本機管理員和網域管理員兩種。本機管理員是指具有對本機電腦的完全管理權限

Oracle SQL中的除法運算詳解 Oracle SQL中的除法運算詳解 Mar 10, 2024 am 09:51 AM

OracleSQL中的除法運算詳解在OracleSQL中,除法運算是一種常見且重要的數學運算運算,用來計算兩個數相除的結果。除法在資料庫查詢中經常用到,因此了解OracleSQL中的除法運算及其用法是資料庫開發人員必備的技能之一。本文將詳細討論OracleSQL中除法運算的相關知識,並提供具體的程式碼範例供讀者參考。一、OracleSQL中的除法運算

linux系統呼叫system()函數詳解 linux系統呼叫system()函數詳解 Feb 22, 2024 pm 08:21 PM

Linux系統呼叫system()函數詳解系統呼叫是Linux作業系統中非常重要的一部分,它提供了一種與系統核心互動的方式。其中,system()函數是常用的系統呼叫函數之一。本文將詳細介紹system()函數的使用方法,並提供對應的程式碼範例。系統呼叫的基本概念系統呼叫是使用者程式與作業系統核心互動的一種方式。使用者程式透過呼叫系統呼叫函數來請求作業系統

PHP模運算子的作用及用法詳解 PHP模運算子的作用及用法詳解 Mar 19, 2024 pm 04:33 PM

PHP中的模運算子(%)是用來取得兩個數值相除的餘數的。在本文中,我們將詳細討論模運算子的作用及用法,並提供具體的程式碼範例來幫助讀者更好地理解。 1.模運算子的作用在數學中,當我們將一個整數除以另一個整數時,就會得到一個商和一個餘數。例如,當我們將10除以3時,商數為3,餘數為1。模運算子就是用來取得這個餘數的。 2.模運算子的用法在PHP中,使用%符號來表示模

C++ 函式繼承詳解:如何偵錯繼承中出現的錯誤? C++ 函式繼承詳解:如何偵錯繼承中出現的錯誤? May 02, 2024 am 09:54 AM

繼承錯誤調試技巧:確保正確的繼承關係。使用偵錯器逐步執行程式碼,檢查變數值。確保正確使用virtual修飾符。檢查隱藏的繼承帶來的菱形繼承問題。檢查抽象類別中未實現的純虛函數。

Linux的curl指令詳解 Linux的curl指令詳解 Feb 21, 2024 pm 10:33 PM

Linux的curl命令詳解摘要:curl是一種強大的命令列工具,用於與伺服器進行資料通訊。本文將介紹curl指令的基本用法,並提供實際的程式碼範例,幫助讀者更好地理解和應用該指令。一、curl是什麼? curl是命令列工具,用於發送和接收各種網路請求。它支援多種協議,如HTTP、FTP、TELNET等,並提供了豐富的功能,如檔案上傳、檔案下載、資料傳輸、代

C++ 函式繼承詳解:如何理解繼承中的「is-a」與「has-a」關係? C++ 函式繼承詳解:如何理解繼承中的「is-a」與「has-a」關係? May 02, 2024 am 08:18 AM

C++函式繼承詳解:掌握「is-a」和「has-a」關係什麼是函式繼承?函數繼承是C++中一種將衍生類別中定義的方法與基底類別中定義的方法關聯起來的技術。它允許衍生類別存取和重寫基底類別的方法,從而擴展了基底類別的功能。 「is-a」和「has-a」關係在函數繼承中,「is-a」關係指派生類別是基底類別的子類型,也就是說,衍生類別「繼承」了基底類別的特性和行為。 「has-a」關係指派生類別包含對基底類別物件的參考或指針,也就是說,衍生類別「擁有」了基底類別物件。語法以下是如何實作函數繼承的語法:classDerivedClass:pu

See all articles