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
类没有覆盖它。