TS 前篇 03:类基础
零基础第三课:类成员、继承与 super、public/private/protected、readonly、参数属性、访问器与抽象类,对齐官方 TypeScript Handbook。
0. 学习目标(可验证)
- 能定义一个带属性、构造函数、方法的类并实例化
- 能用 extends 继承并用 super 调用父类构造函数
- 能区分 public/private/protected 并理解 readonly
1. 一句话理解
类 = “图纸”:属性是零件、构造函数是组装流程、方法是功能。
new一次就按图纸造出一个实例对象。
2. 第一个类
class Greeter {
greeting: string;
constructor(message: string) {
this.greeting = message;
}
greet(): string {
return "Hello, " + this.greeting;
}
}
let greeter = new Greeter("world");
console.log(greeter.greet()); // "Hello, world"
讲解:
class声明类;greeting: string是属性声明(要写类型)。constructor是构造函数:new时自动执行,负责初始化属性。greet(): string是方法;类内访问成员必须写this.。new Greeter("world")创建实例:先分配对象,再执行构造函数。
3. 继承:extends 与 super
class Animal {
name: string;
constructor(theName: string) {
this.name = theName;
}
move(distanceInMeters: number = 0): void {
console.log(`${this.name} moved ${distanceInMeters}m.`);
}
}
class Snake extends Animal {
constructor(name: string) {
super(name); // 必须先调用父类构造函数
}
move(distanceInMeters = 5): void {
console.log("Slithering...");
super.move(distanceInMeters);
}
}
let sam = new Snake("Sammy");
sam.move(); // Slithering... Sammy moved 5m.
讲解:
extends让子类继承父类的属性与方法。- 子类有构造函数时必须先调用
super(...),且在访问this之前调用。 - 子类可以重写(override)父类方法;
super.move(...)显式调用父类版本。 - 声明为父类类型的变量可以装子类实例(
let a: Animal = new Snake("S")),调用方法时执行的是子类版本。
4. 访问修饰符:public / private / protected
class Person {
public name: string; // 默认就是 public,可省略
private secret: string; // 仅本类内部可访问
protected title: string; // 本类与子类可访问,外部不可
constructor(name: string, secret: string, title: string) {
this.name = name;
this.secret = secret;
this.title = title;
}
}
class Employee extends Person {
getTitle(): string {
return this.title; // protected:子类可以访问
}
}
let howard = new Employee("Howard", "s3cr3t", "Engineer");
console.log(howard.name); // 合法
// console.log(howard.secret); // 报错:private
// console.log(howard.title); // 报错:protected
讲解:
- 三个修饰符控制可见性:
public谁都能访问(默认)、private只有本类、protected本类 + 子类。 - TypeScript 的
private是编译期约束;运行时字段仍然存在,但编译器阻止外部访问。 private/protected成员参与类型兼容判断:两个形状相同的类,private 字段来源不同则不兼容。
5. readonly 与参数属性
// readonly:初始化后不可再赋值
class Octopus {
readonly name: string;
readonly numberOfLegs: number = 8;
constructor(theName: string) {
this.name = theName;
}
}
// 参数属性:声明 + 赋值一步完成
class Octopus2 {
readonly numberOfLegs: number = 8;
constructor(readonly name: string) {}
}
讲解:
readonly属性必须在声明处或构造函数里初始化,之后不可赋值。- 构造函数参数前加
readonly/private/public/protected,就自动变成同名的类属性——这叫”参数属性”,少写两行代码。 constructor(readonly name: string)等价于”声明name属性 + 在构造函数里this.name = name”。
6. 访问器(get/set)、静态成员与抽象类
// getter/setter:拦截属性读写
class Employee2 {
private _fullName: string = "";
get fullName(): string {
return this._fullName;
}
set fullName(newName: string) {
if (newName.length > 10) throw new Error("名字太长");
this._fullName = newName;
}
}
// static:挂在类上而不是实例上
class Grid {
static origin = { x: 0, y: 0 };
}
console.log(Grid.origin); // 通过类名访问
// abstract:抽象类不能 new,抽象方法必须由子类实现
abstract class Department {
constructor(public name: string) {}
abstract printMeeting(): void;
}
class AccountingDepartment extends Department {
constructor() {
super("Accounting");
}
printMeeting(): void {
console.log("Meeting at 10am.");
}
}
讲解:
get/set把”读属性”和”写属性”变成可控逻辑(如长度校验);只有get没有set的属性自动视为只读。static成员属于类本身,用类名.成员访问,不随实例复制。abstract class是”半成品图纸”:抽象方法只写签名,子类必须实现;抽象类本身不能被new。constructor(public name: string)是参数属性与修饰符的组合写法。
7. 动手试试
入门版(必做)
- 写一个
Animal类 +Dog extends Animal(带bark()方法),验证继承的方法与属性。 - 给一个类加
private字段,尝试从类外访问,观察编译错误。 - 用参数属性重写一个”构造函数里赋值”的类。
进阶版(选做)
- 写一个带
set fullName长度校验的类,并测试超长赋值抛错。 - 写一个抽象类
Shape(抽象方法area(): number),用两个子类实现并计算面积。
8. 一句话记住
类 = 属性 + 构造函数 + 方法;继承用
extends且先super();private藏起来、protected传后代、readonly定死、abstract留作业。
下一篇进入泛型基础。