1. 类继承与多态的本质解析
面向对象编程中,继承和多态就像建筑行业的模块化施工。当我在2013年第一次用TypeScript实现电商系统商品类体系时,深刻体会到这种设计模式的威力。继承允许我们像搭积木一样构建类层次结构,而多态则让不同子类对象能像变形金刚那样根据场景切换形态。
1.1 继承的DNA复制机制
继承的核心是代码复用,就像生物遗传中的DNA复制。假设我们要开发游戏角色系统:
class Character {
constructor(public name: string, public hp: number) {}
attack(target: Character) {
console.log(`${this.name} attacks ${target.name}!`);
}
}
class Warrior extends Character {
constructor(name: string, hp: number, public stamina: number) {
super(name, hp);
}
shieldBash() {
console.log(`${this.name} performs shield bash!`);
}
}
这里
Warrior
继承了
Character
的所有属性和方法,就像子代继承了父代的基因。
super()
调用相当于在说"先执行父类的构造流程"。我曾在项目中遇到过
npm error class extends value undefined
报错,根本原因是忘记导出父类或者循环引用导致父类未初始化。
1.2 多态的变形金刚特性
多态让不同类型的对象对相同方法调用产生不同行为,就像USB接口可以连接不同设备:
class Mage extends Character {
castSpell() {
console.log(`${this.name} casts fireball!`);
}
// 方法重写
attack() {
this.castSpell();
}
}
const chars: Character[] = [
new Warrior('Conan', 100, 50),
new Mage('Gandalf', 80)
];
chars.forEach(char => char.attack());
// 输出:
// Conan attacks undefined!
// Gandalf casts fireball!
注意这里
attack()
方法在子类中被重写,这就是典型的多态表现。在2016年开发金融系统时,我们利用这种特性实现了不同支付渠道的统一接口处理。
2. TypeScript中的高级继承模式
2.1 抽象类与接口继承
TypeScript通过
abstract
关键字支持抽象类,这就像建筑的设计蓝图:
abstract class Animal {
abstract makeSound(): void;
move(): void {
console.log('Moving...');
}
}
class Dog extends Animal {
makeSound() {
console.log('Woof!');
}
}
接口继承则更灵活,可以实现多重继承:
interface Flyable {
fly(): void;
}
interface Swimmable {
swim(): void;
}
class Duck implements Flyable, Swimmable {
fly() { /*...*/ }
swim() { /*...*/ }
}
在2020年的物联网项目中,我们使用接口继承为不同传感器设备创建了统一的配置接口。
2.2 构造器继承的陷阱
当子类继承父类时,构造器处理需要特别注意:
class Parent {
constructor(public id: number) {}
}
class Child extends Parent {
constructor(id: number, public name: string) {
super(id); // 必须放在最前面
}
}
我曾踩过的坑:
-
忘记调用
super()会导致编译错误 -
在
super()之前访问this会报错 - 参数类型不匹配会导致运行时异常
3. 多态在工程实践中的应用
3.1 策略模式实现
多态最常见的应用就是策略模式。在电商促销系统中:
interface DiscountStrategy {
calculate(price: number): number;
}
class RegularDiscount implements DiscountStrategy {
calculate(price: number) {
return price * 0.9;
}
}
class VIPDiscount implements DiscountStrategy {
calculate(price: number) {
return price * 0.7;
}
}
class Order {
constructor(private strategy: DiscountStrategy) {}
applyDiscount(price: number) {
return this.strategy.calculate(price);
}
}
这种设计使得新增折扣类型时无需修改现有代码,符合开闭原则。
3.2 依赖注入中的多态
在现代框架如NestJS中,多态是依赖注入的核心:
interface Logger {
log(message: string): void;
}
class FileLogger implements Logger {
log(message: string) {
// 写入文件
}
}
class ConsoleLogger implements Logger {
log(message: string) {
console.log(message);
}
}
class AppService {
constructor(private readonly logger: Logger) {}
doSomething() {
this.logger.log('Action performed');
}
}
通过接口抽象,我们可以轻松切换日志实现,这在单元测试时特别有用。
4. 常见问题与性能优化
4.1 原型链污染问题
JavaScript的继承基于原型链,不当操作会导致原型污染:
class SafeArray extends Array {
// 重写危险方法
get reverse() {
throw new Error('Operation not allowed');
}
}
在安全敏感的场景中,需要特别小心继承原生对象。
4.2 内存泄漏排查
继承关系可能导致意外的内存引用。使用Chrome DevTools的Memory面板可以分析:
- 拍快照对比继承前后的内存变化
-
检查
retainer链条找到意外引用 -
特别注意闭包中的
this引用
4.3 性能优化技巧
-
方法缓存 :频繁调用的方法可以缓存
class Optimized { private _cachedResult: any; heavyCompute() { return this._cachedResult || (this._cachedResult = /*...*/); } } -
避免深层继承 :建议不超过3层继承链
-
使用组合替代继承 :对于非is-a关系优先使用组合
5. 现代JavaScript中的类继承
5.1 私有字段与继承
ES2022的私有字段(
#field
)在继承中有特殊表现:
class Parent {
#secret = 123;
getSecret() {
return this.#secret;
}
}
class Child extends Parent {
tryGetSecret() {
return this.#secret; // 编译错误
// return this.getSecret(); // 正确方式
}
}
私有字段的真正私有性使得子类无法直接访问,必须通过父类方法。
5.2 静态块初始化
ES2022引入了静态初始化块:
class DBConnector {
static config: Record<string, any>;
static {
try {
this.config = loadConfig();
} catch (err) {
this.config = { default: true };
}
}
}
这在需要复杂静态初始化时非常有用,比如连接数据库或加载配置文件。
6. TypeScript特有特性
6.1 装饰器与继承
装饰器可以修改类的继承行为:
function logCreation(target: any) {
return class extends target {
constructor(...args: any[]) {
super(...args);
console.log(`Instance created at ${new Date()}`);
}
};
}
@logCreation
class MyClass {}
这种技术在ORM框架中广泛使用,比如TypeORM的实体装饰器。
6.2 条件类型与继承
TypeScript的类型系统可以基于继承关系进行条件判断:
type IsSubclass<T, U> = T extends U ? true : false;
type Test = IsSubclass<Warrior, Character>; // true
这在高级类型编程中非常有用,比如实现类型安全的工厂模式。
7. 实战:构建插件系统
让我们用继承和多态实现一个插件系统:
abstract class Plugin {
abstract init(config: any): void;
abstract execute(input: any): any;
}
class LoggerPlugin extends Plugin {
init(config: { level: string }) {
// 初始化日志级别
}
execute(message: string) {
console.log(`[LOG] ${message}`);
}
}
class PluginManager {
private plugins: Plugin[] = [];
register(plugin: Plugin) {
this.plugins.push(plugin);
}
runAll(input: any) {
return this.plugins.map(p => p.execute(input));
}
}
这种架构使得系统可以动态加载不同功能的插件,而核心逻辑保持不变。在开发VS Code扩展时,我就采用了类似的设计。

969

被折叠的 条评论
为什么被折叠?



