当前位置: 首页 > news >正文

类魔方 :多变组合,灵活复用

文章目录

  • 一、类的基础
    • 1. 类的基本结构与语法
      • 1. 类的定义与实例化
      • 2. 成员变量(属性)
      • 3. 构造函数(Constructor)
      • 4. 成员方法
    • 2. 访问修饰符
      • 1. 基本访问规则
      • 2. 子类对父类方法的重写
      • 3. 构造函数的访问修饰符
      • 4. 参数属性与继承
      • 总结
    • 3. 接口与实现
      • 1、接口的定义(Declaration)
      • 2、类对接口的实现(Implementation)
      • 3、接口与抽象类的区别
      • 4、多接口实现
      • 5、接口的核心作用
      • 总结
    • 4. 静态成员与实例成员
      • 1. 静态成员(Static Members)
      • 2. 实例成员(Instance Members)
      • 对比
      • 注意
    • 5. 访问器(Getter/Setter)
      • 1. 基本语法
      • 2. 核心特点
      • 3. 使用场景
      • 4. 注意事项
    • 6. 类的高级特性
      • 1、泛型类(Generic Classes)
        • 作用
        • 语法
      • 2、装饰器(Decorators)
        • 作用
        • 核心类型
      • 3、参数属性(Parameter Properties)
        • 作用
        • 语法
      • 关键点总结
  • 二、类的提高
    • 1. 抽象类(Abstract Classes)
      • 1. 抽象类的定义
      • 2. 抽象类的特点
      • 3. 抽象属性
    • 2. 类的多态(Polymorphism)
      • 1. 多态的核心实现方式
        • 场景一:继承与方法重写
        • 场景二:接口实现
      • 2. 多态的关键特性
      • 3. 多态与重载的区别
      • 4. 多态的优势
    • 3. 类的类型兼容性
      • 1. 核心规则
        • (1)子类与父类的兼容性
        • (2)无继承关系的类
      • 2. 成员的兼容性细节
        • (1)属性兼容性
        • (2)方法兼容性
      • 3. 特殊成员的影响
        • (1)私有 / 受保护成员
        • (2)构造函数
      • 总结
    • 4. 类的受保护构造函数
      • 1. 受保护构造函数的作用
        • (1)禁止外部实例化,强制通过子类创建对象
      • 2. 受保护构造函数的特点
      • 3. 实际应用场景
        • (1)工厂模式
        • (2)防止基础类被滥用
      • 5. 注意事项
    • 5. 类的私有字段(ES2022 语法)
      • 1. 基本语法
      • 2. 与 `private` 修饰符的区别
      • 3. 私有字段的特性
        • (1)运行时强制访问限制
        • (2)不允许子类访问
        • (3)严格的名称唯一性
      • 4. 私有字段的应用场景
        • (1)数据封装与隐藏
        • (2)防止命名冲突
        • (3)实现类内部状态
      • 5. 注意事项
    • 6. 类的构造函数重载(Constructor Overloading)
      • 1. 构造函数重载的语法
      • 2. 重载签名与实现的约束
      • 3. 与可选参数的对比
      • 4. 常见应用场景
        • (1)创建工厂模式
        • (2)处理不同数据格式
      • 5. 注意事项
    • 7. 类的静态块(Static Blocks)
      • 1. 静态块的特性
        • (1)仅执行一次
        • (2)可访问私有字段
        • (3)支持多个静态块
      • 2. 与静态构造函数的对比
      • 3. 注意事项

一、类的基础

1. 类的基本结构与语法

1. 类的定义与实例化

类是对象的蓝图,通过class关键字定义,使用new关键字创建实例。

class Person {// 类的主体
}const person = new Person(); // 创建类的实例

2. 成员变量(属性)

类中可以定义数据字段(属性),用于存储实例或类的状态。

class Person {name: string; // 实例属性age: number;constructor(name: string, age: number) {this.name = name;this.age = age;}static species = "Homo sapiens"; // 静态属性,属于类本身
}const person = new Person("abc",18);
person.name = "Alice"; // 访问实例属性
person.age = 30;// console.log(person.species);console.log(Person.species); // 访问静态属性,通过类名调用

3. 构造函数(Constructor)

特殊方法,用于初始化对象的状态,创建实例时自动调用。

class Person {name: string;age: number;constructor(name: string, age: number) {this.name = name; // 使用this引用当前实例this.age = age;}
}const person = new Person("Bob", 25); // 传递参数给构造函数

4. 成员方法

类中定义的函数,用于实现对象的行为。

class Person {constructor(public name: string, public age: number) {}greet() {return `Hello, I'm ${this.name}, ${this.age} years old.`;}static describeSpecies() { // 静态方法return "All persons are Homo sapiens.";}
}const person = new Person("Charlie", 35);
console.log(person.greet()); // 调用实例方法
console.log(Person.describeSpecies()); // 调用静态方法

2. 访问修饰符

  1. public(默认)

    • 可被任意访问(类内部、子类、外部)。
    class Person {public name: string; // 显式声明publicage: number; // 默认public
    }
    
  2. private

    • 只能在类内部访问,子类和外部不可访问。
    class Employee {private salary: number; // 私有属性getSalary() {return this.salary; // 类内部可访问}
    }
    
  3. protected

    • 类内部和子类可访问,外部不可访问。
    class Animal {protected name: string;
    }
    class Dog extends Animal {getName() {return this.name; // 子类可访问}
    }
    
  4. readonly

    • 属性只能在初始化时赋值,之后不可修改。
    class Point {readonly x: number;constructor(x: number) {this.x = x; // 初始化时赋值}
    }
    

1. 基本访问规则

修饰符类内部子类外部
public
protected
private

2. 子类对父类方法的重写

  • 子类可以重写父类的方法,但访问修饰符不能更严格。
    • 例如:父类的protected方法,子类重写时只能是protectedpublic
class Vehicle {protected startEngine() {console.log("Engine started");}
}class Car extends Vehicle {// ✅ 正确:重写为 public(更宽松)public startEngine() {console.log("Car engine started");}// ❌ 错误:重写为 private(更严格)// private startEngine() { ... }
}
// <html>TS2415: Class 'Car' incorrectly extends base class 'Vehicle'.<br/>Property 'startEngine' is private in type 'Car' but not in type 'Vehicle'.

3. 构造函数的访问修饰符

  • private构造函数:禁止外部实例化,可用于实现单例模式、工具类。

单例模式(Singleton Pattern)是一种创建型设计模式,确保一个类只有一个实例,并提供一个全局访问点来获取这个实例。

class Singleton {private static instance: Singleton; // 静态属性存储唯一实例private constructor() {// 私有构造函数,防止外部实例化}static getInstance(): Singleton {if (!Singleton.instance) {Singleton.instance = new Singleton(); // 首次调用时创建实例}return Singleton.instance;}// 其他方法public someMethod() {console.log("Singleton method called");}
}// const s = new Singleton(); // 错误:无法实例化// 使用示例
const instance1 = Singleton.getInstance();
const instance2 = Singleton.getInstance();
console.log(instance1 === instance2); // true,两个引用指向同一个实例
  • protected构造函数:禁止外部实例化,但允许子类继承。
// 受保护构造函数示例
class Base {protected constructor() {}
}class Derived extends Base {} // 正确:子类可继承
// const b = new Base(); // 错误:无法直接实例化

4. 参数属性与继承

  • 参数属性(构造函数中直接定义的属性)同样受访问修饰符控制。
class Person {constructor(public name: string,     // 公开属性protected age: number,   // 受保护属性private salary: number   // 私有属性) {}
}class Employee extends Person {constructor(name: string, age: number, salary: number, public department: string) {super(name, age, salary);}showInfo() {console.log(`${this.name}, ${this.age}`); // 可访问 public/protected// console.log(this.salary); // 错误:无法访问 private}
}

总结

场景publicprotectedprivate
类内部访问
子类访问
外部访问
子类重写方法的限制不能更严格不可重写
构造函数修饰可实例化仅子类可继承仅类内部可实例化

3. 接口与实现

在面向对象编程中,接口(Interface)用于定义类的公共契约(方法、属性的声明),而实现(Implementation) 则是类对接口中声明的具体代码实现。

1、接口的定义(Declaration)

接口仅规定类必须包含的成员(方法名、参数类型、返回类型等),不包含具体实现逻辑。

interface Animal {name: string; // 属性声明speak(): string; // 方法声明(无函数体)age?: number; // 可选属性(非必填)
}

2、类对接口的实现(Implementation)

类通过 implements 关键字声明遵循某个接口,并必须实现接口中所有必填成员

// 实现接口 Animal
class Dog implements Animal {// 必须实现接口中的 name 属性constructor(public name: string) {} // 必须实现接口中的 speak 方法speak(): string { return "Woof!";}// 可选属性(可实现也可不实现)age?: number; 
}

3、接口与抽象类的区别

特性接口(Interface)抽象类(Abstract Class)
是否可实例化完全抽象,不可实例化不可实例化(但可包含具体实现的属性或方法)
成员类型仅声明(属性、方法、索引签名等)可包含抽象成员和具体实现
继承方式implements(可实现多个接口)extends(仅继承一个抽象类)
强制约束必须实现所有声明的成员必须实现抽象成员,可继承具体成员

4、多接口实现

一个类可以实现多个接口,需满足所有接口的要求。

interface Swimmable {swim(): string;
}interface Runable {run(speed: number): string;
}// 实现两个接口
class Duck implements Animal, Swimmable, Runable { }

5、接口的核心作用

  1. 规范契约:确保不同类遵循统一的方法 / 属性定义,便于团队协作和代码维护。
  2. 类型检查:编译器会强制类实现接口声明的成员,避免运行时错误。
  3. 解耦依赖:代码可依赖接口而非具体类,提高可扩展性(如依赖倒置原则)。
// 依赖接口而非具体类(面向接口编程)
function introduce(animal: Animal) {return `${animal.name} says ${animal.speak()}`;
}introduce(new Dog("Buddy")); // "Buddy says Woof!"
introduce(new Duck("Donald")); // "Donald says Quack!"

总结

  • 接口是契约的抽象声明,规定 “必须有什么”;
  • 类的实现是契约的具体落地,规定 “如何实现”;
  • 通过 implements 关键字关联接口与类,确保类型安全和代码规范。

4. 静态成员与实例成员

1. 静态成员(Static Members)

  • 归属:属于类本身,而非类的实例。
  • 访问方式:通过类名直接调用(如 Class.staticMethod())。
  • 特点:
    • 所有实例共享同一静态成员。
    • 不能访问实例属性或方法(无 this 指向实例)。

2. 实例成员(Instance Members)

  • 归属:属于类的实例,每个实例独立拥有。
  • 访问方式:通过实例调用(如 instance.method())。
  • 特点:
    • 每个实例的属性值可不同。
    • 可访问实例属性和静态成员。

对比

特性静态成员实例成员
归属类本身类的实例
访问方式Class.memberinstance.member
this 指向类本身当前实例
共享性所有实例共享同一值每个实例独立存储
使用场景工具方法、全局配置、单例模式对象的状态和行为

注意

  • 静态属性初始化:静态属性在类加载时初始化,早于实例创建。
  • 静态方法限制:静态方法无法直接访问实例属性,需通过参数传递实例。

5. 访问器(Getter/Setter)

1. 基本语法

  • Getter:读取属性值时调用的方法(get 关键字)。
  • Setter:设置属性值时调用的方法(set 关键字)。
class User {// private _age: number;constructor(private _age: number) {}// Getter:读取 age 属性get age() {return this._age;}// Setter:设置 age 属性set age(value: number) {if (value < 0) throw new Error("Age cannot be negative");this._age = value;}
}const user = new User(5);// user.age = -30; // 如果没有try-catch块调用 setter,触发错误,程序在此处终止// try 块中抛出错误位置之后的代码会被中断,但catch块和后续代码会继续执行。
try {user.age = -30;
} catch (err: unknown) {if (err instanceof Error) {console.error(err.message + ' failed');} else {console.error(`Unexpected error type: ${err}`);}
} finally {console.log(user.age);
}console.log(user.age); 

2. 核心特点

特性GetterSetter
语法get propertyName()set propertyName(value)
参数无参数必须有且仅有一个参数
返回值必须有返回值不能有返回值(void)
调用方式像访问属性一样调用(无括号)像赋值一样调用(obj.prop = value

3. 使用场景

  • 数据验证:在赋值时校验数据有效性(如示例中的年龄不能为负数)。
  • 计算属性:动态计算属性值(如根据其他属性计算)。
  • 访问控制:隐藏内部实现细节,提供受控访问。

4. 注意事项

  • 必须成对出现:在严格模式下,通常需要同时定义 getter 和 setter。
  • 与普通属性的区别:访问器会拦截属性的读写操作,而普通属性直接存储值。
  • 兼容性:编译为 ES5 及以下版本时,需使用 Object.defineProperty 实现。

6. 类的高级特性

  • 泛型类:支持类的属性或方法使用泛型类型。
  • 装饰器(Decorators):修改类的行为(实验性特性)。
  • 参数属性:在构造函数中直接定义并初始化属性。

1、泛型类(Generic Classes)

作用

使类的属性、方法支持动态类型,提高复用性。

语法

在类名后声明泛型参数(如 <T>),并在成员中使用。

class Box<T> { // 泛型类:T 为任意类型constructor(public value: T) {}showType(): string {return `Type of value: ${typeof this.value}`;}
}// 使用示例
const numberBox = new Box<number>(123); // 类型为 number
const stringBox = new Box<string>("Hello"); // 类型为 stringconsole.log(numberBox.showType()); // "Type of value: number"

2、装饰器(Decorators)

作用

在不修改类本身的情况下,动态添加或修改类的行为(需启用 experimentalDecorators 编译选项)。

核心类型
  • 类装饰器:修改类的原型或静态属性。
  • 方法装饰器:修改方法的特性(如添加日志)。
  • TypeScript 类型检查:仅基于类的静态定义(如 User 类中并未声明 isLogged)。
  • 装饰器的动态性:装饰器在运行时修改类的原型,这种修改无法被 TypeScript 的类型系统自动捕获。(报错但可运行)
// 其作用是在运行时修改类的原型(prototype),为类的所有实例添加一个 isLogged 属性
function Logged(constructor: Function) {constructor.prototype.isLogged = true;
}@Logged 
class User {constructor(public name: string) {}
}// 使用接口合并(Interface Merging)扩展User类的类型定义
interface User {isLogged: boolean; // 显式声明装饰器添加的属性
}const user = new User("Alice");
console.log(user.isLogged); // ✅ 类型检查通过// isLogged 是原型属性(非实例属性),所有实例共享同一个值。
// 实例可直接访问该属性,但修改它会创建实例自己的属性(遮蔽原型属性)。
const user1 = new User("Bob");
const user2 = new User("Charlie");console.log(user1.isLogged); // true(继承自原型)
user1.isLogged = false; // 仅修改 user1 自己的属性console.log(user1.isLogged); // false(实例属性)User.prototype.isLogged = false;console.log(user2.isLogged); // false(原型属性改变)

3、参数属性(Parameter Properties)

作用

在构造函数中直接声明并初始化属性,省略属性声明代码。

语法

在构造函数参数前添加访问修饰符(public/private/protected/readonly)。

class Person {// 等价于:// public name: string;// private age: number;// readonly id: string;constructor(public name: string,     // 公开属性private age: number,      // 私有属性readonly id: string       // 只读属性) {}
}

关键点总结

特性核心语法 / 示例用途 / 场景
泛型类class Box<T> { value: T; }集合、工具类、类型安全封装
装饰器@Decorator 语法,修改类行为日志、权限控制、元数据管理
参数属性constructor(public prop: Type) {}快速初始化简单属性

二、类的提高

1. 抽象类(Abstract Classes)

在 TypeScript 中,抽象类(Abstract Class)是一种不能直接实例化的类,它为子类提供通用的属性和方法定义。抽象类可以包含抽象成员(未实现的方法或属性),强制子类必须实现这些成员。

1. 抽象类的定义

使用 abstract 关键字声明类和抽象成员。子类通过 extends 关键字继承抽象类,并实现所有抽象成员。

abstract class Animal {// 实例属性(非抽象)constructor(public name: string) {}// 抽象方法:子类必须实现abstract speak(): string;// 具体方法:子类可直接继承或重写move(): string {return `${this.name} is moving`;}
}class Dog extends Animal {// 实现抽象方法 speakspeak(): string {return "Woof!";}// 重写具体方法 move(可选)move(): string {return `${this.name} is running`;}
}

2. 抽象类的特点

特性抽象类普通类
实例化❌ 不能直接实例化✅ 可以实例化
抽象成员✅ 允许包含抽象方法 / 属性❌ 不能包含抽象成员
子类约束子类必须实现所有抽象成员无强制约束
使用场景定义基类接口,强制子类遵循契约具体实现类

3. 抽象属性

抽象类中可以声明抽象属性(无初始值,子类必须实现)。

abstract class Vehicle {abstract wheels: number; // 抽象属性abstract start(): void;getWheelCount(): number {return this.wheels; // 使用抽象属性}
}class Car extends Vehicle {wheels = 4; // 实现抽象属性start(): void {console.log("Car started");}
}

2. 类的多态(Polymorphism)

在面向对象编程中,多态(Polymorphism)指的是不同类的对象对同一消息(方法调用)作出不同响应的能力。TypeScript 通过继承接口实现来支持多态,核心是子类重写父类方法实现接口方法。以下是关键概念和示例:

1. 多态的核心实现方式

场景一:继承与方法重写

父类定义方法,子类 重写(Override) 该方法以实现不同逻辑。

class Animal {speak(): string {return "Animal sound";}
}class Dog extends Animal {override speak(): string { // 重写父类方法return "Woof!";}
}class Cat extends Animal {override speak(): string { // 重写父类方法return "Meow!";}
}
场景二:接口实现

不同类实现同一接口,对接口方法作出不同实现。

interface Shape {calculateArea(): number;
}class Circle implements Shape {constructor(public radius: number) {}calculateArea(): number {return Math.PI * this.radius ** 2;}
}class Rectangle implements Shape {constructor(public width: number, public height: number) {}calculateArea(): number {return this.width * this.height;}
}

2. 多态的关键特性

特性说明
类型兼容性子类对象可赋值给父类(如 Animal animal = new Dog();)或const a: Animal = new Dog();
动态绑定方法调用的具体实现由对象的运行时类型决定,而非编译时类型
解耦性高层模块依赖抽象(父类或接口),而非具体子类,符合依赖倒置原则

里氏替换原则(LSP)

“子类对象必须能够替换其父类对象而不影响程序的正确性。”

3. 多态与重载的区别

特性多态(Polymorphism)重载(Overloading)
定义不同对象对同一方法的不同实现同一类中多个同名方法,参数列表不同
实现方式继承 / 接口实现方法参数类型、数量或顺序不同
作用统一接口,差异化实现方便调用,适应不同参数组合

4. 多态的优势

  1. 可扩展性:新增子类无需修改现有逻辑(符合开闭原则)。
  2. 代码复用:通过父类或接口统一管理子类对象。
  3. 类型安全:TypeScript 编译器确保子类实现所有必要方法。

3. 类的类型兼容性

在 TypeScript 中,类的类型兼容性基于结构子类型(Structural Subtyping),而非名义子类型(Nominal Subtyping)。这意味着:
若两个类的结构(属性和方法)兼容,则它们类型兼容,无论是否存在继承关系。

1. 核心规则

(1)子类与父类的兼容性
  • 子类 → 父类:始终兼容(子类包含父类的所有成员)。
  • 父类 → 子类:不兼容(父类缺少子类的额外成员)。
(2)无继承关系的类

若两个类的结构相同,则它们类型兼容

class Point1 {x: number;y: number;
}class Point2 {x: number;y: number;
}let p1: Point1 = new Point2(); // ✅ 结构相同,类型兼容

2. 成员的兼容性细节

(1)属性兼容性
  • 必须存在:目标类型的所有属性必须在源类型中存在。
  • 类型兼容:对应属性的类型必须兼容(协变)。
class A { x: number; }
class B { x: number; y: string; }let a: A = new B(); // ✅ B 包含 A 的所有属性
let b: B = new A(); // ❌ A 缺少 B 的 y 属性
(2)方法兼容性
  • 参数逆变:源类型的方法参数类型必须是目标类型的超类型。

如果类型 A 可以赋值给类型 B,则称 AB 的子类型(A ≤ B,或 BA 的超类型(B ≥ A

  • 返回值协变:源类型的返回值类型必须是目标类型的子类型。

协变规则(返回值类型)

源类型(被赋值的类型)的返回值类型必须是目标类型(赋值的目标类型)返回值类型的子类型

class Animal { move(): void {} }
class Dog extends Animal { move(distance: number): void {} }let a: Animal = new Dog(); // ✅ Dog.move 参数更具体(兼容逆变)
let d: Dog = new Animal(); // ❌ Animal.move 缺少 distance 参数(不兼容)

3. 特殊成员的影响

(1)私有 / 受保护成员

若类包含私有或受保护成员,则仅当这些成员来自同一类定义时,类型才兼容:

class Animal {private name: string; // 私有成员
}class Dog extends Animal {}
class Cat {private name: string; // 不同类的私有成员
}let d: Dog = new Animal(); // ✅ 父子类私有成员同源
let a: Animal = new Cat(); // ❌ 私有成员不同源
(2)构造函数

构造函数不影响类型兼容性,仅关注实例成员:

class A { constructor(x: number) {} }
class B { constructor(y: string) {} }let a: A = new B(); // ✅ 构造函数不参与类型检查

总结

TypeScript 的类类型兼容性基于结构匹配,而非名义关系:

  1. 子类与父类:子类可赋值给父类,但反之不可。
  2. 无继承关系的类:结构相同则兼容。
  3. 私有 / 受保护成员:必须来自同一类定义。
  4. 方法参数:遵循逆变规则(源类型参数更宽泛)。

4. 类的受保护构造函数

在 TypeScript 中,受保护构造函数(protected constructor用于限制类的实例化范围,仅允许在当前类或其子类中调用。当不需要继承其他类时,受保护构造函数可以实现。

1. 受保护构造函数的作用

(1)禁止外部实例化,强制通过子类创建对象
class Animal {protected constructor(public name: string) {} // 受保护构造函数
}// ❌ 错误:无法在类外部实例化受保护构造函数的类
// const animal = new Animal("Buddy"); class Dog extends Animal {constructor(name: string) {super(name); // ✅ 子类中可调用父类的受保护构造函数}
}const dog = new Dog("Buddy"); // ✅ 正确:通过子类实例化

2. 受保护构造函数的特点

修饰符实例化范围子类继承接口实现
public任何地方均可实例化允许无影响
protected仅当前类或子类中可实例化允许无影响
private仅当前类内部可实例化禁止继承无影响

3. 实际应用场景

(1)工厂模式

通过受保护构造函数隐藏实例化细节,强制通过工厂方法创建对象:

class User {protected constructor(public id: number, public name: string) {}static createUser(name: string): User {// 内部逻辑生成 idreturn new User(1, name); // ✅ 类内部可调用受保护构造函数}
}// ❌ 错误:无法直接实例化
// const user = new User(1, "Alice"); const user = User.createUser("Alice"); // ✅ 通过工厂方法创建
(2)防止基础类被滥用

确保只有特定子类可以创建实例:

class Vehicle {protected constructor(public wheels: number) {}
}class Car extends Vehicle {constructor() {super(4); // 汽车固定为 4 个轮子}
}class Bicycle extends Vehicle {constructor() {super(2); // 自行车固定为 2 个轮子}
}

5. 注意事项

  1. 子类必须调用 super()
    子类构造函数中必须显式调用父类的受保护构造函数,否则编译报错。

  2. 类型兼容性
    包含受保护构造函数的类仍可作为父类型使用:

    const vehicle: Vehicle = new Car(); // 多态赋值,类型兼容
    
  3. 无法通过接口强制约束
    接口不能包含构造函数,因此无法通过接口强制子类实现受保护构造函数。

5. 类的私有字段(ES2022 语法)

在 TypeScript 中,私有字段(Private Fields) 是 ES2022 引入的语法,使用 # 前缀声明类的私有成员。与传统的 private 修饰符不同,私有字段具有更强的封装性,在运行时直接限制访问

1. 基本语法

使用 # 前缀声明私有字段,只能在类内部访问:

class Person {#name: string; // 私有字段constructor(name: string) {this.#name = name;}greet() {return `Hello, my name is ${this.#name}`; // 类内部可访问}
}const person = new Person("Alice");
console.log(person.greet()); // "Hello, my name is Alice"
console.log(person.#name); // 错误:私有字段无法在类外部访问

2. 与 private 修饰符的区别

特性私有字段 (#field)private 修饰符
访问限制运行时强制限制,外部无法访问仅编译时检查,运行时可绕过
作用域严格绑定到当前类子类中不可访问,但可通过反射访问
兼容性ES2022+,需编译降级所有 TypeScript 版本支持
命名冲突每个类独有,不与父类 / 子类冲突子类可定义同名成员(隐藏父类)

3. 私有字段的特性

(1)运行时强制访问限制

私有字段在运行时直接阻止外部访问:

class Secret {#key = "12345";getKey() {return this.#key;}
}const secret = new Secret();
console.log(secret.getKey()); // "12345"
console.log(secret.#key); // ❌ 语法错误:无法访问私有字段
(2)不允许子类访问

即使在子类中也无法直接访问父类的私有字段:

class Parent {#privateField = "Parent secret";
}class Child extends Parent {accessParentField() {console.log(this.#privateField); // ❌ 错误:无法访问父类私有字段}
}
(3)严格的名称唯一性

同一类中不能定义同名的私有字段和公共属性:

class Example {#name = "Alice";name = "Bob"; // ❌ 错误:名称冲突(即使一个是私有字段)
}

4. 私有字段的应用场景

(1)数据封装与隐藏

保护敏感数据不被外部直接访问:

class BankAccount {#balance: number;constructor(initialBalance: number) {this.#balance = initialBalance;}deposit(amount: number) {this.#balance += amount;}// 仅通过公共方法访问私有字段getBalance() {return this.#balance;}
}
(2)防止命名冲突

在继承体系中避免子类意外覆盖父类成员:

class Base {#internalId = "base123";
}class Sub extends Base {#internalId = "sub456"; // ✅ 允许,私有字段名称独立
}
(3)实现类内部状态

存储仅用于类内部逻辑的状态:

class Counter {#count = 0;increment() {this.#count++;}getCount() {return this.#count;}
}

5. 注意事项

  1. 不能在类外声明类型
    私有字段的类型只能在类内部声明:

    class User {#age: number; // ✅ 正确
    }// ❌ 错误:无法在类外为私有字段添加类型注解
    type User = {#age: number;
    }
    
  2. 性能考虑
    私有字段在 JavaScript 中通过 WeakMap 实现,可能略慢于普通属性,但通常可忽略不计。

6. 类的构造函数重载(Constructor Overloading)

在 TypeScript 中,构造函数重载(Constructor Overloading)允许类的构造函数根据不同参数类型和数量提供多种调用方式。虽然 TypeScript 支持构造函数重载语法,但实现时只能有一个具体的构造函数,需要在单个实现中处理所有重载签名。

1. 构造函数重载的语法

  • 声明多个重载签名:定义不同参数组合的构造函数类型。
  • 实现单个构造函数:使用最宽泛的参数类型实现,并在内部处理所有情况。
class Point {x: number;y: number;// 重载签名 1:无参数constructor();// 重载签名 2:两个数值参数constructor(x: number, y: number);// 重载签名 3:对象参数constructor(coords: { x: number; y: number });// 实际实现(必须兼容所有重载签名)constructor(x?: number | { x: number; y: number }, y?: number) {if (typeof x === 'object') {this.x = x.x;this.y = x.y;} else {// 空值合并操作符:若 x 为 undefined 或 null,则返回 0,否则返回 x。this.x = x ?? 0;this.y = y ?? 0;}}
}// 使用不同重载创建实例
const p1 = new Point(); // x=0, y=0
const p2 = new Point(1, 2); // x=1, y=2
const p3 = new Point({ x: 3, y: 4 }); // x=3, y=4
  • x 是对象时:从对象中提取 xy 属性赋值给实例。
  • x 是其他类型(如数值)时:直接使用 xy 参数,若参数为 undefinednull 则默认赋值为 0

2. 重载签名与实现的约束

  1. 实现必须兼容所有重载签名
    实现的参数类型必须是所有重载签名参数类型的联合类型。

    class Person {// 重载签名constructor(name: string);constructor(name: string, age: number);// 实现(参数类型必须能处理所有重载情况)constructor(name: string, age?: number) {// ...}
    }
    
  2. 重载签名不参与实际实现
    重载签名仅用于类型检查,不能包含实现代码。

    class Example {constructor(x: number); // ❌ 错误:重载签名不能有实现体constructor(x: number) { this.x = x; } // ✅ 正确:实现必须单独声明
    }
    

3. 与可选参数的对比

构造函数重载 vs 可选参数:

方式适用场景示例
重载参数类型或数量差异较大,需要明确类型constructor(x: number); constructor(obj: { x: number });
可选参数参数数量固定,部分参数可选constructor(x: number, y?: number);

4. 常见应用场景

(1)创建工厂模式

通过不同参数创建不同类型的实例:

class Logger {// 重载签名constructor(source: string);constructor(config: { source: string; level: 'info' | 'error' });// 实现constructor(sourceOrConfig: string | { source: string; level: 'info' | 'error' }) {if (typeof sourceOrConfig === 'string') {// 处理字符串参数} else {// 处理对象参数}}
}
(2)处理不同数据格式

根据传入数据的不同格式初始化对象:

class User {id: number;name: string;// 重载签名constructor(data: { id: number; name: string });constructor(id: number, name: string);// 实现constructor(arg1: number | { id: number; name: string }, arg2?: string) {if (typeof arg1 === 'number') {this.id = arg1;this.name = arg2!;} else {this.id = arg1.id;this.name = arg1.name;}}
}

5. 注意事项

  1. 重载顺序很重要
    TypeScript 会按重载签名的顺序匹配参数,因此应将更具体的签名放在前面

    class Example {constructor(x: number); // 更具体的签名constructor(x: any);    // 更宽泛的签名constructor(x: any) { /* ... */ }
    }
    
  2. 避免过度重载
    过多的重载签名会使代码复杂,优先考虑使用可选参数联合类型简化。

    // 简化前
    constructor(x: number);
    constructor(x: string);// 简化后
    constructor(x: number | string);
    
  3. 无法重载仅返回类型不同的构造函数
    构造函数重载必须基于参数差异,不能仅依赖返回类型。

7. 类的静态块(Static Blocks)

在 TypeScript 和 JavaScript 中,类的静态块(Static Blocks) 是 ES2022 引入的语法,用于在类定义时执行一次性的初始化逻辑。静态块内可以访问类的私有字段,且只会在类被加载时执行一次。

1. 静态块的特性

(1)仅执行一次

静态块在类被加载时自动执行,且只执行一次:

class App {static instances = 0;static {console.log("App class initialized");this.instances++;}
}// 输出: "App class initialized"
console.log(App.instances); // 1
console.log(App.instances); // 1(不会重复执行静态块)
(2)可访问私有字段

静态块可以直接访问类的私有静态字段

(3)支持多个静态块

类中可以定义多个静态块,它们会按顺序执行:

class Sequence {static #counter = 0;static {this.#counter = 100; // 初始化计数器}static {this.#counter++; // 递增计数器}static getNext() {return this.#counter++;}
}console.log(Sequence.getNext()); // 101

2. 与静态构造函数的对比

静态块 vs 静态属性初始化:

方式执行时机访问私有字段执行顺序
静态块类加载时✅ 可以按定义顺序执行
静态属性初始化类加载时✅ 可以按定义顺序执行
静态方法调用显式调用时✅ 可以按需调用

3. 注意事项

  1. 不能直接返回值
    静态块没有返回值,其目的是执行副作用(如初始化静态属性)。
  2. 与类的实例无关
    静态块仅在类加载时执行,与实例化对象无关。即使类从未被实例化,静态块也会执行。

相关文章:

  • Estimation(估算):业务分析师的“不确定性对抗术”
  • Python黑魔法与底层原理揭秘:突破语言边界的深度探索
  • 【VMware】开启「共享文件夹」
  • 软件架构之-论软件系统架构评估以及应用
  • 批量下载AlphaFold结构
  • 2.1.2
  • LORA 微调 - LoRA 介绍与 LoRA 微调指南
  • vue3 elementplus tabs切换实现
  • 程序代码篇---python获取http界面上按钮或者数据输入
  • “二维前缀和”算法原理及模板
  • 亚马逊海卖助手有什么功能?海卖助手的主要功能与特点分析
  • mybatis中的resultMap的association及collectio的使用
  • Python训练营---Day29
  • 飞机飞行控制系统补偿模型辨识报告
  • 估分啦~全国青少年信息素养大赛部分赛项已考完~图形化/算法创意实践
  • 量子计算在金融科技中的应用前景
  • 系统思考:IT企业项目困境分析
  • ImgShrink:摄影暗房里的在线图片压缩工具开发记
  • 第5章 监控与回归测试:日志收集 · 代码覆盖率 · 静态分析 · 质量门
  • Vue 3 动态 ref 的使用方式(表格)
  • AI快速迭代带来知识焦虑,褚君浩院士提出“四维能力模型”
  • 国际观察丨美中东政策生变,以色列面临艰难选择
  • 特写|银耳种植“北移”到沧州盐山,村民入伙可年增收4万元
  • 多少Moreless:向世界展示现代中式家具的生活美学
  • 北方将现今年首场大范围高温天气,山西河南山东陕西局地可超40℃
  • 美国务卿鲁比奥抵达会场,将参加俄乌会谈