JavaScript 中的多型性
本教程圍繞 JavaScript 中的多型性展開。
在本課中,我們將學習什麼是多型性。為什麼使用它?它是如何工作的,我們如何在 JavaScript 中實現它?
什麼是 JavaScript 中的多型性
多型是物件導向程式設計(OOP)的核心概念之一。在進入細節之前瞭解多型的字面含義是很好的。
Poly
一詞的意思是許多
或多個
,morph
的意思是形式
。因此,多型性
意味著多種形式
或多種形式
。
繼承允許我們將父類的屬性(屬性和方法)繼承到子類中。多型性通過覆蓋執行各種任務來獲得這些方法的優勢。
它進一步分為兩類:
- 靜態多型
- 動態多型
靜態多型發生在編譯時,也稱為編譯時多型。與方法過載相同。
由於 JavaScript 不是編譯語言,它本身不支援靜態多型。但我們可以通過使用替代品來模仿它。
另一方面,動態多型發生在執行時(程式執行期間),也稱為執行時多型。JavaScript 是解釋型語言,使得方法覆蓋可以實現動態多型性。
我們將在標題為 JavaScript 中的多型性工作
的第三部分中看到這兩個示例。
在 JavaScript 中為什麼使用多型性
由於以下優點而使用多型性。
- 我們可以重用已經編寫和測試過的程式碼。
- 由於使用了多型,程式碼很容易除錯。
- 節省時間,提高工作質量。
JavaScript 中的多型性如何工作
本節將學習靜態和動態多型在 JavaScript 中的工作原理。
JavaScript 中的靜態多型是如何工作的
靜態多型性與函式過載相同(我們可以擁有多個名稱完全相同但引數不同的方法),但這在 JavaScript 中不受支援,因為由於名稱相同,JavaScript 會用最新的方法覆蓋舊方法。
但是有一種方法可以模擬靜態多型性。請參閱下面給出的示例程式碼。
class calculator{
constructor(){}
sum(num1, num2, num3){
if(num3 != undefined){
console.log("Sum is called with three parameters");
return num1+num2+num3;
}else{
console.log("Sum is called with two parameters");
return num1+num2;
}
}
}
const c = new calculator();
console.log(c.sum(2,3));
console.log(c.sum(2,3,4));
輸出:
"Sum is called with two parameters"
5
"Sum is called with three parameters"
9
我們在上面給出的程式碼中使用 if-else
條件來模擬靜態多型性。我們呼叫 sum()
方法兩次。我們在第一次呼叫時傳遞兩個引數,在第二次呼叫時傳遞三個引數。
檢查類 calculator
是否第三個引數是 undefined
。如果是,則為兩個引數執行 sum()
;否則,對於三個引數。
假設我們為四個引數呼叫 sum()
方法。現在,程式碼如下所示。
class calculator{
constructor(){}
sum(num1, num2, num3, num4){
if(num4 != undefined){
console.log("Sum is called with four parameters");
return num1+num2+num3+num4;
}else if(num3 != undefined){
console.log("Sum is called with three parameters");
return num1+num2+num3;
}else{
console.log("Sum is called with two parameters");
return num1+num2;
}
}
}
const c = new calculator();
console.log(c.sum(2,3));
console.log(c.sum(2,3,4));
console.log(c.sum(2,3,4,5));
輸出:
"Sum is called with two parameters"
5
"Sum is called with three parameters"
9
"Sum is called with four parameters"
14
在 JavaScript 中動態多型性如何工作
動態多型性允許我們以多種方式執行單個操作。例如,我們有一個函式 calculateArea()
,我們可以在子類中重寫它來計算圓和矩形的面積。
繼承是動態多型的前提。讓我們通過下面的例子來理解。
形狀
類:
class Shape{
calculateArea(){
console.log("shape.calculateArea is called.");
return 0;
}
}
矩形
類:
class Rectangle extends Shape{
constructor(length, width){
super();
this.length = length;
this.width = width;
}
calculateArea(){
console.log("rectangle.calculateArea is called.");
return this.length * this.width;
}
}
圈
類:
class Circle extends Shape{
constructor(radius){
super();
this.radius = radius;
}
calculateArea(){
console.log("circle.calculateArea is called.");
return Math.PI * this.radius ** 2;
}
}
主要程式碼:
s = new Rectangle(3,4);
console.log(s.calculateArea());
s= new Circle(2);
console.log(s.calculateArea());
輸出:
"rectangle.calculateArea is called."
12
"circle.calculateArea is called."
12.566370614359172
請注意,我們在父類(即 Shape
)和子類(Rectangle
和 Circle
)中具有相同的 calculateArea()
方法,但它們的工作方式不同。這是因為持有不同物件的引用。
s
在此程式碼中包含 Rectangle
類的引用 s = new Rectangle(3,4);
但它在程式碼 s= new Circle(2);
中包含 Circle
類的引用。
我們如何知道變數 s
持有哪個引用?它是在執行時決定的,因為物件是在執行時建立的;這就是為什麼它被稱為動態多型性。
另一個問題是如何找出方法 calculateArea()
是從哪個類呼叫的,因為它存在於父類和所有子類中。
同樣,它取決於在執行時建立的物件。假設 s
持有 Circle
類的物件,而 Circle
已覆蓋 calculateArea()
方法。
在這種情況下,將呼叫 Circle
類中的 calculateArea()
。
假設 Circle
沒有覆蓋 calculateArea()
方法;在這種情況下,calculateArea()
方法將從 Circle
的父級呼叫,即 Shape
。
練習時,從 Circle
類中刪除 calculateArea()
並執行程式碼;你將看到以下輸出。
"rectangle.calculateArea is called."
12
"shape.calculateArea is called."
0
你是否注意到從 Shape
類呼叫 calculateArea()
?這是因為 Circle
類沒有覆蓋它。