摘要面向对象编程OOP是现代软件开发的重要范式。TypeScript 在 JavaScript 原型链的基础上提供了完整的类Class语法和增强特性访问修饰符public/private/protected、抽象类、静态成员、类与接口的配合等。本文从零讲解 TypeScript 中的类并结合封装、继承、多态三大特性帮助你写出结构清晰、可复用的代码。一、前言前三篇文章中我们学习了 TypeScript 的基础类型、函数、对象和接口。现在我们可以为普通的 JavaScript 代码加上类型注解用接口定义对象结构。但是当项目规模增长我们需要更强大的组织代码的方式——面向对象编程OOP。OOP 将数据和对数据的操作封装在一起类通过继承实现代码复用通过多态应对变化。TypeScript 让 JavaScript 的类更加强大和严谨。二、JavaScript 类的演变ES6ES2015为 JavaScript 引入了class语法糖但它本质上还是基于原型链。TypeScript 在此基础上增加了类型系统和面向对象设计的增强特性。ES6 类示例class Animal { constructor(name) { this.name name; } speak() { console.log(${this.name} makes a sound.); } }问题ES6 类没有真正的私有成员只能通过约定_name或Symbol模拟也没有类型约束。TypeScript 填补了这些空白。三、TypeScript 中的类基础3.1 类的基本定义使用class关键字定义类属性需要提前声明类型不像 JavaScript 动态添加。class Person { name: string; // 声明属性实例属性 age: number; constructor(name: string, age: number) { this.name name; this.age age; } greet(): void { console.log(Hello, Im ${this.name}, ${this.age} years old.); } } const p new Person(Alice, 30); p.greet(); // Hello, Im Alice, 30 years old.注意TypeScript 要求在构造函数中明确初始化所有声明了的属性否则会报错除非开启strictPropertyInitialization或使用非空断言!。3.2 构造函数constructor构造函数在new时执行用于初始化实例。参数和返回值的类型注解与普通函数一样。class Student { name: string; grade: number; constructor(name: string, grade: number) { this.name name; this.grade grade; } }如果参数带有默认值或修饰符见下文可以使用更简洁的写法。3.3 实例属性与方法实例属性就是this.xxx实例方法直接定义在类内部。class Counter { count: number 0; // 可以直接赋初始值 increment(): void { this.count; } getValue(): number { return this.count; } }四、访问修饰符public、private、protectedTypeScript 提供了三个修饰符来控制类成员的可见性。修饰符含义public默认值任何地方都可访问private只在当前类内部可访问子类和实例不能访问protected在当前类和子类内部可访问实例不能访问4.1 基本用法class Animal { public name: string; // 公有默认 private age: number; // 私有 protected species: string; // 受保护 constructor(name: string, age: number, species: string) { this.name name; this.age age; this.species species; } private getAge(): number { // 私有方法 return this.age; } public showInfo(): void { console.log(${this.name}, age${this.age}, species${this.species}); console.log(this.getAge()); // 内部可以调用私有方法 } } const dog new Animal(旺财, 3, Canine); console.log(dog.name); // ✅ 公有可访问 // console.log(dog.age); // ❌ 私有报错 // console.log(dog.species); // ❌ 受保护实例不能访问 // dog.getAge(); // ❌ 私有方法子类中的访问class Dog extends Animal { bark(): void { console.log(${this.name} bark!); // ✅ 公有可以 // console.log(this.age); // ❌ 私有子类也不能访问 console.log(this.species); // ✅ 受保护子类可访问 } }4.2 参数属性Parameter Properties—— 简化写法TypeScript 提供了一种语法糖在构造函数参数前直接加上修饰符TS 会自动声明同名的实例属性并赋值。class User { // 等价于先声明 public name: string; 然后在构造函数中 this.name name constructor(public name: string, private age: number) {} info(): string { return ${this.name}, ${this.age}; } } const u new User(张三, 25); console.log(u.name); // ✅ 公有 // console.log(u.age); // ❌ 私有这是非常实用的写法可以减少重复代码。五、继承Inheritance继承是面向对象的核心之一允许子类复用父类的属性和方法并添加自己的特性。5.1 extends 关键字使用extends实现继承。class Vehicle { constructor(public brand: string, public speed: number) {} move(): void { console.log(${this.brand} is moving at ${this.speed} km/h.); } } class Car extends Vehicle { doors: number; constructor(brand: string, speed: number, doors: number) { super(brand, speed); // 必须先调用 super() this.doors doors; } honk(): void { console.log(${this.brand} horn: Beep beep!); } } const myCar new Car(Toyota, 120, 4); myCar.move(); // 继承自父类 myCar.honk(); // 自己的方法关键点子类构造函数中必须调用super(...)执行父类构造函数。super必须在访问this之前调用。5.2 方法重写Override子类可以重写父类的方法覆盖其行为。同时可以在重写的方法中使用super.method()调用父类版本。class Animal { speak(): void { console.log(Animal makes a sound.); } } class Cat extends Animal { speak(): void { console.log(Meow!); } } class Dog extends Animal { speak(): void { super.speak(); // 先调用父类方法 console.log(Woof!); } } new Cat().speak(); // Meow! new Dog().speak(); // Animal makes a sound. Woof!TypeScript 建议在重写的方法上使用override关键字TS 4.3便于明确意图和防止拼写错误。class Bird extends Animal { override speak(): void { // 显式标记 override console.log(Chirp chirp); } }如果没有override但方法名写错本意重写却写成了新方法TS 会给出提示。六、抽象类Abstract Class抽象类是不能被实例化的类用作其他类的基类。它可以包含抽象方法没有实现必须在子类中实现和具体实现。6.1 抽象方法与抽象类使用abstract关键字。abstract class Shape { abstract getArea(): number; // 抽象方法没有方法体 getType(): string { // 普通方法可以有实现 return Shape; } } // const s new Shape(); // ❌ 不能实例化抽象类 class Circle extends Shape { constructor(public radius: number) { super(); } getArea(): number { // 必须实现抽象方法 return Math.PI * this.radius ** 2; } } const c new Circle(5); console.log(c.getArea()); // 78.5398...6.2 抽象类 vs 接口抽象类接口是否可实例化否不适用仅类型是否可以包含实现可以普通方法、属性不可以纯声明TS 中可放属性但无实现继承/实现子类extends单个抽象类类可以实现多个接口用途提供公共基础实现强制子类实现某些行为定义契约完全抽离实现实践建议当多个类有共同的实现逻辑时用抽象类仅定义行为规范时用接口。七、静态成员Static静态成员属于类本身而不是实例。通过类名直接访问。class MathUtils { static PI: number 3.14159; static circleArea(radius: number): number { return this.PI * radius * radius; // 静态方法中 this 指向类本身 } } console.log(MathUtils.PI); // 3.14159 console.log(MathUtils.circleArea(5)); // 78.53975静态成员可以被继承子类也能访问但不能通过实例访问。class ExtendedMath extends MathUtils {} console.log(ExtendedMath.PI); // 继承得到八、类与接口Implements接口定义了一个结构契约类可以通过implements关键字来保证自己符合某个接口。8.1 基本用法interface Drawable { draw(): void; } interface Resizable { resize(width: number, height: number): void; } class Rectangle implements Drawable, Resizable { constructor(public width: number, public height: number) {} draw(): void { console.log(Drawing rectangle ${this.width}x${this.height}); } resize(w: number, h: number): void { this.width w; this.height h; } }注意类可以实现多个接口逗号分隔。如果类没有完全实现接口中的成员TS 会报错。8.2 类与接口的区别接口只描述结构不包含实现也不生成运行时代码。类既描述结构又包含实现会生成 JS 代码。// 接口编译后消失 interface Point { x: number; y: number; } // 类编译后存在 class PointImpl implements Point { constructor(public x: number, public y: number) {} }8.3 接口继承类TypeScript 中接口可以继承类这会继承类的成员包括私有和受保护成员但仅用于类型检查。这种模式常用于将类作为“无形接口”。class Control { private state: any; } interface SelectableControl extends Control { select(): void; } class Button extends Control implements SelectableControl { select(): void {} } // 错误Image 没有继承 Control缺少私有成员 state class Image implements SelectableControl { // ❌ select(): void {} }九、总结本文完整介绍了 TypeScript 中的面向对象编程能力类的基础属性声明与构造函数实例方法与属性访问修饰符public默认、private仅类内、protected类内和子类参数属性简化代码继承extendssuper调用父类方法重写override标记抽象类abstract定义不能实例化的基类抽象方法强制子类实现静态成员通过类名直接调用适用于工具方法或常量类与接口implements让类遵守契约可实现多个接口接口可继承类特殊用法OOP 三大特性封装通过修饰符隐藏内部细节继承复用父类代码多态子类重写方法同一接口不同实现如果这篇文章帮你解决了实操上的困惑别忘记点击点赞、分享也可以留言告诉我你遇到的其它问题我会尽快回复。动手练习是掌握编程最快的方法请务必亲手敲一遍本文的所有示例代码并截图保存你的成果。你的关注是我坚持原创和细节共享的力量来源谢谢大家。
TypeScript 从零基础到精通(四):面向对象编程(类与继承)
摘要面向对象编程OOP是现代软件开发的重要范式。TypeScript 在 JavaScript 原型链的基础上提供了完整的类Class语法和增强特性访问修饰符public/private/protected、抽象类、静态成员、类与接口的配合等。本文从零讲解 TypeScript 中的类并结合封装、继承、多态三大特性帮助你写出结构清晰、可复用的代码。一、前言前三篇文章中我们学习了 TypeScript 的基础类型、函数、对象和接口。现在我们可以为普通的 JavaScript 代码加上类型注解用接口定义对象结构。但是当项目规模增长我们需要更强大的组织代码的方式——面向对象编程OOP。OOP 将数据和对数据的操作封装在一起类通过继承实现代码复用通过多态应对变化。TypeScript 让 JavaScript 的类更加强大和严谨。二、JavaScript 类的演变ES6ES2015为 JavaScript 引入了class语法糖但它本质上还是基于原型链。TypeScript 在此基础上增加了类型系统和面向对象设计的增强特性。ES6 类示例class Animal { constructor(name) { this.name name; } speak() { console.log(${this.name} makes a sound.); } }问题ES6 类没有真正的私有成员只能通过约定_name或Symbol模拟也没有类型约束。TypeScript 填补了这些空白。三、TypeScript 中的类基础3.1 类的基本定义使用class关键字定义类属性需要提前声明类型不像 JavaScript 动态添加。class Person { name: string; // 声明属性实例属性 age: number; constructor(name: string, age: number) { this.name name; this.age age; } greet(): void { console.log(Hello, Im ${this.name}, ${this.age} years old.); } } const p new Person(Alice, 30); p.greet(); // Hello, Im Alice, 30 years old.注意TypeScript 要求在构造函数中明确初始化所有声明了的属性否则会报错除非开启strictPropertyInitialization或使用非空断言!。3.2 构造函数constructor构造函数在new时执行用于初始化实例。参数和返回值的类型注解与普通函数一样。class Student { name: string; grade: number; constructor(name: string, grade: number) { this.name name; this.grade grade; } }如果参数带有默认值或修饰符见下文可以使用更简洁的写法。3.3 实例属性与方法实例属性就是this.xxx实例方法直接定义在类内部。class Counter { count: number 0; // 可以直接赋初始值 increment(): void { this.count; } getValue(): number { return this.count; } }四、访问修饰符public、private、protectedTypeScript 提供了三个修饰符来控制类成员的可见性。修饰符含义public默认值任何地方都可访问private只在当前类内部可访问子类和实例不能访问protected在当前类和子类内部可访问实例不能访问4.1 基本用法class Animal { public name: string; // 公有默认 private age: number; // 私有 protected species: string; // 受保护 constructor(name: string, age: number, species: string) { this.name name; this.age age; this.species species; } private getAge(): number { // 私有方法 return this.age; } public showInfo(): void { console.log(${this.name}, age${this.age}, species${this.species}); console.log(this.getAge()); // 内部可以调用私有方法 } } const dog new Animal(旺财, 3, Canine); console.log(dog.name); // ✅ 公有可访问 // console.log(dog.age); // ❌ 私有报错 // console.log(dog.species); // ❌ 受保护实例不能访问 // dog.getAge(); // ❌ 私有方法子类中的访问class Dog extends Animal { bark(): void { console.log(${this.name} bark!); // ✅ 公有可以 // console.log(this.age); // ❌ 私有子类也不能访问 console.log(this.species); // ✅ 受保护子类可访问 } }4.2 参数属性Parameter Properties—— 简化写法TypeScript 提供了一种语法糖在构造函数参数前直接加上修饰符TS 会自动声明同名的实例属性并赋值。class User { // 等价于先声明 public name: string; 然后在构造函数中 this.name name constructor(public name: string, private age: number) {} info(): string { return ${this.name}, ${this.age}; } } const u new User(张三, 25); console.log(u.name); // ✅ 公有 // console.log(u.age); // ❌ 私有这是非常实用的写法可以减少重复代码。五、继承Inheritance继承是面向对象的核心之一允许子类复用父类的属性和方法并添加自己的特性。5.1 extends 关键字使用extends实现继承。class Vehicle { constructor(public brand: string, public speed: number) {} move(): void { console.log(${this.brand} is moving at ${this.speed} km/h.); } } class Car extends Vehicle { doors: number; constructor(brand: string, speed: number, doors: number) { super(brand, speed); // 必须先调用 super() this.doors doors; } honk(): void { console.log(${this.brand} horn: Beep beep!); } } const myCar new Car(Toyota, 120, 4); myCar.move(); // 继承自父类 myCar.honk(); // 自己的方法关键点子类构造函数中必须调用super(...)执行父类构造函数。super必须在访问this之前调用。5.2 方法重写Override子类可以重写父类的方法覆盖其行为。同时可以在重写的方法中使用super.method()调用父类版本。class Animal { speak(): void { console.log(Animal makes a sound.); } } class Cat extends Animal { speak(): void { console.log(Meow!); } } class Dog extends Animal { speak(): void { super.speak(); // 先调用父类方法 console.log(Woof!); } } new Cat().speak(); // Meow! new Dog().speak(); // Animal makes a sound. Woof!TypeScript 建议在重写的方法上使用override关键字TS 4.3便于明确意图和防止拼写错误。class Bird extends Animal { override speak(): void { // 显式标记 override console.log(Chirp chirp); } }如果没有override但方法名写错本意重写却写成了新方法TS 会给出提示。六、抽象类Abstract Class抽象类是不能被实例化的类用作其他类的基类。它可以包含抽象方法没有实现必须在子类中实现和具体实现。6.1 抽象方法与抽象类使用abstract关键字。abstract class Shape { abstract getArea(): number; // 抽象方法没有方法体 getType(): string { // 普通方法可以有实现 return Shape; } } // const s new Shape(); // ❌ 不能实例化抽象类 class Circle extends Shape { constructor(public radius: number) { super(); } getArea(): number { // 必须实现抽象方法 return Math.PI * this.radius ** 2; } } const c new Circle(5); console.log(c.getArea()); // 78.5398...6.2 抽象类 vs 接口抽象类接口是否可实例化否不适用仅类型是否可以包含实现可以普通方法、属性不可以纯声明TS 中可放属性但无实现继承/实现子类extends单个抽象类类可以实现多个接口用途提供公共基础实现强制子类实现某些行为定义契约完全抽离实现实践建议当多个类有共同的实现逻辑时用抽象类仅定义行为规范时用接口。七、静态成员Static静态成员属于类本身而不是实例。通过类名直接访问。class MathUtils { static PI: number 3.14159; static circleArea(radius: number): number { return this.PI * radius * radius; // 静态方法中 this 指向类本身 } } console.log(MathUtils.PI); // 3.14159 console.log(MathUtils.circleArea(5)); // 78.53975静态成员可以被继承子类也能访问但不能通过实例访问。class ExtendedMath extends MathUtils {} console.log(ExtendedMath.PI); // 继承得到八、类与接口Implements接口定义了一个结构契约类可以通过implements关键字来保证自己符合某个接口。8.1 基本用法interface Drawable { draw(): void; } interface Resizable { resize(width: number, height: number): void; } class Rectangle implements Drawable, Resizable { constructor(public width: number, public height: number) {} draw(): void { console.log(Drawing rectangle ${this.width}x${this.height}); } resize(w: number, h: number): void { this.width w; this.height h; } }注意类可以实现多个接口逗号分隔。如果类没有完全实现接口中的成员TS 会报错。8.2 类与接口的区别接口只描述结构不包含实现也不生成运行时代码。类既描述结构又包含实现会生成 JS 代码。// 接口编译后消失 interface Point { x: number; y: number; } // 类编译后存在 class PointImpl implements Point { constructor(public x: number, public y: number) {} }8.3 接口继承类TypeScript 中接口可以继承类这会继承类的成员包括私有和受保护成员但仅用于类型检查。这种模式常用于将类作为“无形接口”。class Control { private state: any; } interface SelectableControl extends Control { select(): void; } class Button extends Control implements SelectableControl { select(): void {} } // 错误Image 没有继承 Control缺少私有成员 state class Image implements SelectableControl { // ❌ select(): void {} }九、总结本文完整介绍了 TypeScript 中的面向对象编程能力类的基础属性声明与构造函数实例方法与属性访问修饰符public默认、private仅类内、protected类内和子类参数属性简化代码继承extendssuper调用父类方法重写override标记抽象类abstract定义不能实例化的基类抽象方法强制子类实现静态成员通过类名直接调用适用于工具方法或常量类与接口implements让类遵守契约可实现多个接口接口可继承类特殊用法OOP 三大特性封装通过修饰符隐藏内部细节继承复用父类代码多态子类重写方法同一接口不同实现如果这篇文章帮你解决了实操上的困惑别忘记点击点赞、分享也可以留言告诉我你遇到的其它问题我会尽快回复。动手练习是掌握编程最快的方法请务必亲手敲一遍本文的所有示例代码并截图保存你的成果。你的关注是我坚持原创和细节共享的力量来源谢谢大家。