10.1 多态的基础
多态是面向对象编程 (OOP) 的核心概念之一。简单来说,多态让不同类的对象可以通过同一个接口来处理数据。在 JavaScript 的语境中,这意味着不同的对象可以有相同名字的方法,而且这些方法可以在不知道对象具体类型的情况下被调用。
多态的类型
在 JavaScript 中,主要的多态类型有:
1. 特设多态 (ad-hoc polymorphism):
- 方法的多态性,为不同类型的对象调用相同名字的方法
- 例子包括函数重载和运算符重载(这在 JavaScript 中不直接支持,但可以模拟)
2. 子类型多态 (Subtype polymorphism):
- 子类型多态或包含,当不同类的对象继承于同一个基类时,可以作为基类的对象来处理
- 这是通过继承和接口实现的主要多态类型
多态的优势:
- 简化代码: 多态允许编写更灵活和通用的代码,可以处理不同类型的对象而无需知道它们的具体类型。
- 扩展性: 多态使得在系统中添加新类型和行为变得更容易,而不需要修改已有代码。
- 支持: 多态有助于更好地分工并提高代码的可读性和可维护性。
10.2 JavaScript 中的多态示例
通过继承实现的子类型多态
示例 1:使用一个接口处理不同类型的对象
在这个例子中,playWithAnimal函数接收一个Animal类型的对象并调用makeSound方法。继承自Animal的Dog和Cat对象重写了makeSound方法,每个对象调用该方法时会得到不同的结果。
class Animal {
makeSound() {
console.log('Some generic sound');
}
}
class Dog extends Animal {
makeSound() {
console.log('Woof!');
}
}
class Cat extends Animal {
makeSound() {
console.log('Meow!');
}
}
function playWithAnimal(animal) {
animal.makeSound();
}
const dog = new Dog();
const cat = new Cat();
playWithAnimal(dog); // 输出: Woof!
playWithAnimal(cat); // 输出: Meow!
10.3 通过接口实现的多态 (Duck Typing)
JavaScript 没有内建接口支持,像 TypeScript 或 Java 那样。取而代之的是使用所谓“鸭子类型” (Duck Typing) 的方式。这意味着如果一个对象有必要的方法和属性,就认为它符合该接口,而不论其具体类型或继承关系。
鸭子规则 (duck): 如果某物看起来像鸭子、游起来像鸭子、叫起来像鸭子,那它大概就是鸭子。
示例 2:使用鸭子类型
在这个例子中,takeOff函数接收任何有fly方法的对象。Bird和Airplane对象都实现了fly方法,所以可以传递给takeOff。
class Bird {
fly() {
console.log('Flying...');
}
}
class Airplane {
fly() {
console.log('Jet engine roaring...');
}
}
function takeOff(flyingObject) {
flyingObject.fly();
}
const bird = new Bird();
const airplane = new Airplane();
takeOff(bird); // 输出: Flying...
takeOff(airplane); // 输出: Jet engine roaring...
10.4 通过函数实现的多态
在 JavaScript 中,函数是第一类对象,可以传递并用于实现多态行为。
示例 3:通过函数实现多态
在这个例子中,greet函数接收另一个函数作为参数并调用它。这就可以使用不同的函数来执行不同的操作。
function greetMorning() {
console.log('Good morning!');
}
function greetEvening() {
console.log('Good evening!');
}
function greet(greetingFunction) {
greetingFunction();
}
greet(greetMorning); // 输出: Good morning!
greet(greetEvening); // 输出: Good evening!
10.5 通过方法重载的多态 (模拟)
JavaScript 不直接支持方法重载,像某些其他编程语言那样。不过,可以通过函数参数和类型检查来模拟方法重载。
示例 4:模拟方法重载
在这个例子中,add方法接收两个参数,并根据它们的类型执行不同的操作。如果参数是数字,方法就相加它们。如果参数是数组,方法就合并它们。
class Calculator {
add(a, b) {
if (typeof a === 'number' && typeof b === 'number') {
return a + b;
} else if (Array.isArray(a) && Array.isArray(b)) {
return a.concat(b);
} else {
throw new Error('Invalid arguments');
}
}
}
const calc = new Calculator();
console.log(calc.add(1, 2)); // 输出: 3
console.log(calc.add([1, 2], [3, 4])); // 输出: [1, 2, 3, 4]
GO TO FULL VERSION