静态成员
本教程共 80 篇 · 第 35 篇 · 更新于 2026-08-10 · 约 10 分钟阅读
本节目标:掌握 static 属性、方法和静态块的使用,理解静态成员属于类本身而非实例,学会用静态工厂方法等实用模式。
前面四章讲的都是实例成员——每个 new 出来的对象有自己的属性、自己的方法。但有些东西是所有实例共享的,或者跟实例根本没关系,这时候就该用静态成员了。
静态属性
用 static 关键字声明的属性属于类本身,不属于任何实例:
class Counter {
static count = 0; // 静态属性:属于类
constructor() {
Counter.count++; // 通过类名访问
}
}
const c1 = new Counter();
const c2 = new Counter();
const c3 = new Counter();
console.log(Counter.count); // 3 —— 直接通过类名访问
// console.log(c1.count); // ❌ 实例上访问不到
Counter.count 是存储在类上的,所有实例共享同一个值。这跟实例属性完全不同——每个实例的属性是独立的。
静态方法
静态方法同样属于类,通过类名调用:
class MathUtils {
static clamp(value: number, min: number, max: number): number {
return Math.max(min, Math.min(value, max));
}
static lerp(a: number, b: number, t: number): number {
return a + (b - a) * t;
}
}
console.log(MathUtils.clamp(5, 0, 10)); // 5
console.log(MathUtils.clamp(-3, 0, 10)); // 0
console.log(MathUtils.clamp(15, 0, 10)); // 10
console.log(MathUtils.lerp(0, 100, 0.5)); // 50
静态方法最常见的用途就是工具函数集合。你不需要创建实例就能用它们。
Note如果你来自 C# 或 Java,可能会想写一个”静态类”——所有成员都是 static 的类。在 TypeScript 里不需要这样做。直接用一个普通对象或者模块的顶层函数就够了。TypeScript 没有
static class语法。
静态成员也可以有访问修饰符
static 可以和 public、protected、private 组合:
class Database {
private static connection: string | null = null;
static connect(url: string): void {
Database.connection = url;
console.log(`已连接到 ${url}`);
}
static getConnection(): string {
if (Database.connection === null) {
throw new Error("未连接数据库");
}
return Database.connection;
}
}
Database.connect("postgres://localhost:5432/mydb");
console.log(Database.getConnection());
// console.log(Database.connection); // ❌ private 不能外部访问
静态成员的 this 指向
静态方法里的 this 指向的是类本身,而不是实例:
class MyClass {
static x = 0;
static printX(): void {
console.log(this.x); // this 指向 MyClass 这个构造函数
}
}
MyClass.printX(); // 0
这个特性在继承时特别有用:
class Base {
static greeting = "Hello";
static greet(): void {
console.log(this.greeting);
}
}
class Derived extends Base {
static greeting = "你好";
}
Base.greet(); // "Hello"
Derived.greet(); // "你好" —— this 指向的是 Derived!
因为 Derived.greet() 是通过 Derived 调用的,所以 this 指向 Derived,访问的是 Derived.greeting。
静态成员被继承
子类可以继承父类的静态成员:
class Base {
static getClassName(): string {
return "Base";
}
}
class Derived extends Base {
// 继承了 getClassName()
}
console.log(Derived.getClassName()); // "Base"
注意这里 Derived.getClassName() 可以正常调用,但返回值是 "Base"——因为方法体里硬编码了返回字符串。如果你想让方法感知当前类,可以用 this.constructor.name,但在静态方法里 this 指向的是类本身(构造函数),所以得用 this.name:
class Base {
static getClassName(): string {
return this.name; // this 指向调用时的类
}
}
class Derived extends Base {}
console.log(Base.getClassName()); // "Base"
console.log(Derived.getClassName()); // "Derived"
静态块
从 ES2022(TypeScript 4.4+)开始,类里可以写静态块(static block)。它是一段在类定义时立即执行的代码,有自己的作用域,能访问类的私有字段:
class Config {
static #apiUrl: string;
static {
// 复杂的初始化逻辑可以放在这里
try {
const env = process.env.NODE_ENV;
Config.#apiUrl = env === "production"
? "https://api.prod.example.com"
: "https://api.dev.example.com";
} catch {
Config.#apiUrl = "https://api.dev.example.com";
}
}
static getApiUrl(): string {
return Config.#apiUrl;
}
}
console.log(Config.getApiUrl());
静态块适合的场景:
- 静态属性需要复杂的初始化逻辑(多行、条件判断、try-catch)。
- 需要访问私有静态字段来做初始化。
- 多个静态属性之间存在依赖关系,需要按顺序初始化。
Tip如果静态属性只用一个表达式就能初始化,直接在声明时赋值就行,不需要静态块。静态块是用来处理”一个表达式搞不定”的情况。
静态工厂方法
静态方法的一个经典用法是静态工厂方法——用静态方法创建实例,而不是直接 new:
class User {
constructor(
public readonly id: number,
public readonly name: string,
public readonly role: string
) {}
static createAdmin(name: string): User {
return new User(Math.floor(Math.random() * 10000), name, "admin");
}
static createGuest(): User {
return new User(0, "Guest", "guest");
}
static fromJSON(json: string): User {
const data = JSON.parse(json);
return new User(data.id, data.name, data.role);
}
}
const admin = User.createAdmin("Alice");
console.log(admin.role); // "admin"
const guest = User.createGuest();
console.log(guest.name); // "Guest"
const user = User.fromJSON('{"id": 1, "name": "Bob", "role": "user"}');
console.log(user.name); // "Bob"
工厂方法比直接 new 好在:
- 命名清晰:
User.createAdmin("Alice")比new User(Math.random(), "Alice", "admin")更易读。 - 可以加逻辑:创建之前做校验、创建之后做额外处理。
- 可以返回子类:工厂方法内部可以根据条件返回不同的子类实例。
静态成员不能做的事
不能用实例访问
class Example {
static value = 42;
}
const e = new Example();
// console.log(e.value); // ❌ 实例上不存在
泛型类的静态成员不能引用类型参数
class Box<T> {
// static defaultValue: T; // ❌ 静态成员不能引用类泛型参数
}
为什么?因为类型在运行时被擦除了。Box<string> 和 Box<number> 在运行时的 Box.defaultValue 是同一个内存槽——如果 T 可以用,到底该是 string 还是 number?所以 TypeScript 直接禁止。
特殊的静态名称
类的静态成员不能跟 Function.prototype 上的属性重名。name、length、call、apply、bind 等都不能用作静态成员名:
class S {
// static name = "S"; // ❌ 跟 Function.name 冲突
}
小结
static成员属于类本身,通过类名访问,不通过实例。- 静态方法里的
this指向调用时的类(构造函数)。 - 静态成员可以被继承,
this在继承链中指向调用类。 - 静态块
static {}用于复杂的类初始化逻辑。 - 静态工厂方法提供更语义化的创建实例方式。
- 泛型类的静态成员不能引用类型参数。
- 静态名不能跟
Function.prototype的固有属性冲突。
类本身的结构已经讲得差不多了。接下来要解决一个关键问题:类怎么跟接口配合——类实现接口。