Статические члены существуют в единственном экземпляре — на уровне класса. К ним обращаются через имя класса, а не через экземпляр.
class MathUtils {
static readonly PI = 3.14159265358979
static circleArea(r: number): number {
return MathUtils.PI * r * r
}
static clamp(value: number, min: number, max: number): number {
return Math.min(Math.max(value, min), max)
}
}
// Вызов через класс, не через экземпляр:
console.log(MathUtils.PI) // 3.14159...
console.log(MathUtils.circleArea(5)) // 78.53...
console.log(MathUtils.clamp(15, 0, 10)) // 10
// const m = new MathUtils()
// m.circleArea(5) // Ошибка TS: Property 'circleArea' does not exist on instanceclass User {
static count = 0
static readonly maxUsers = 1000
readonly id: number
name: string
constructor(name: string) {
if (User.count >= User.maxUsers) {
throw new Error('Достигнут лимит пользователей')
}
User.count++
this.id = User.count
this.name = name
}
static reset() {
User.count = 0
}
}
new User('Алексей') // User.count = 1
new User('Ольга') // User.count = 2
console.log(User.count) // 2 — общее для всех экземпляровСтатические члены идеально подходят для паттерна Singleton — гарантируют, что существует ровно один экземпляр:
class Database {
private static instance: Database | null = null
private connected = false
private constructor(private url: string) {} // приватный конструктор!
static getInstance(url: string): Database {
if (!Database.instance) {
Database.instance = new Database(url)
}
return Database.instance
}
connect() {
if (!this.connected) {
console.log(`Подключение к ${this.url}`)
this.connected = true
}
}
}
const db1 = Database.getInstance('postgres://localhost:5432/mydb')
const db2 = Database.getInstance('postgres://other-host') // та же строка игнорируется
console.log(db1 === db2) // true — один и тот же экземплярTypeScript поддерживает статические блоки инициализации:
class Config {
static readonly defaults: Record<string, string>
static readonly version: string
static {
// Сложная инициализация статических полей
Config.defaults = { lang: 'ru', theme: 'light', timeout: '5000' }
Config.version = process.env.APP_VERSION ?? '1.0.0'
console.log('Config инициализирован')
}
}class Counter {
// Статические — общие для всех
static totalCreated = 0
static totalDestroyed = 0
// Экземплярные — у каждого свои
private count = 0
constructor() {
Counter.totalCreated++
}
increment() { this.count++ }
getValue() { return this.count }
destroy() { Counter.totalDestroyed++ }
static getActiveCount() {
return Counter.totalCreated - Counter.totalDestroyed
}
}
const c1 = new Counter()
const c2 = new Counter()
c1.increment()
c1.increment()
c2.increment()
console.log(c1.getValue()) // 2 — только у c1
console.log(c2.getValue()) // 1 — только у c2
console.log(Counter.totalCreated) // 2 — общееПаттерн Singleton и фабрика объектов с использованием статических полей: реестр соединений с БД
// Singleton через статические поля — один экземпляр на всё приложение
class ConnectionPool {
static #instance = null
static #defaultConfig = {
host: 'localhost',
port: 5432,
maxSize: 10,
timeout: 5000,
}
#connections = []
#config
#stats = { created: 0, released: 0, failed: 0 }
// Приватный конструктор — нельзя вызвать напрямую
constructor(config) {
this.#config = { ...ConnectionPool.#defaultConfig, ...config }
}
// Единственная точка доступа к экземпляру
static getInstance(config = {}) {
if (!ConnectionPool.#instance) {
ConnectionPool.#instance = new ConnectionPool(config)
console.log('[Pool] Создан новый пул соединений')
}
return ConnectionPool.#instance
}
static reset() {
ConnectionPool.#instance = null
console.log('[Pool] Пул сброшен')
}
// Получить соединение из пула
acquire() {
if (this.#connections.length < this.#config.maxSize) {
const conn = {
id: ++this.#stats.created,
host: this.#config.host,
port: this.#config.port,
createdAt: Date.now(),
busy: true,
}
this.#connections.push(conn)
console.log(`[Pool] Соединение #${conn.id} создано`)
return conn
}
throw new Error(`Пул заполнен (max: ${this.#config.maxSize})`)
}
// Вернуть соединение в пул
release(conn) {
const idx = this.#connections.findIndex(c => c.id === conn.id)
if (idx === -1) throw new Error('Соединение не принадлежит пулу')
this.#connections.splice(idx, 1)
this.#stats.released++
console.log(`[Pool] Соединение #${conn.id} освобождено`)
}
get stats() {
return {
...this.#stats,
active: this.#connections.length,
config: { ...this.#config },
}
}
}
// Registry — статический реестр через Map
class ServiceRegistry {
static #services = new Map()
static #count = 0
static register(name, factory) {
if (ServiceRegistry.#services.has(name)) {
throw new Error(`Сервис "${name}" уже зарегистрирован`)
}
ServiceRegistry.#services.set(name, { factory, count: 0 })
ServiceRegistry.#count++
console.log(`[Registry] Зарегистрирован: ${name}`)
}
static get(name) {
const entry = ServiceRegistry.#services.get(name)
if (!entry) throw new Error(`Сервис "${name}" не найден`)
entry.count++
return entry.factory()
}
static get registeredCount() { return ServiceRegistry.#count }
static list() { return [...ServiceRegistry.#services.keys()] }
}
// --- Демонстрация ---
console.log('=== Singleton: ConnectionPool ===')
const pool1 = ConnectionPool.getInstance({ host: 'db.prod.com', maxSize: 5 })
const pool2 = ConnectionPool.getInstance({ host: 'другой хост' }) // игнорируется
console.log('Это один экземпляр:', pool1 === pool2) // true
const conn1 = pool1.acquire()
const conn2 = pool1.acquire()
console.log('Активных соединений:', pool1.stats.active) // 2
pool1.release(conn1)
console.log('После release:', pool1.stats)
console.log('\n=== Статический реестр сервисов ===')
ServiceRegistry.register('logger', () => ({ log: console.log }))
ServiceRegistry.register('formatter', () => ({ format: (s) => s.toUpperCase() }))
console.log('Зарегистрировано:', ServiceRegistry.registeredCount) // 2
console.log('Список:', ServiceRegistry.list())
const logger = ServiceRegistry.get('logger')
logger.log('Сервис получен из реестра')
try {
ServiceRegistry.register('logger', () => {})
} catch (e) {
console.log('Ошибка:', e.message)
}Статические члены существуют в единственном экземпляре — на уровне класса. К ним обращаются через имя класса, а не через экземпляр.
class MathUtils {
static readonly PI = 3.14159265358979
static circleArea(r: number): number {
return MathUtils.PI * r * r
}
static clamp(value: number, min: number, max: number): number {
return Math.min(Math.max(value, min), max)
}
}
// Вызов через класс, не через экземпляр:
console.log(MathUtils.PI) // 3.14159...
console.log(MathUtils.circleArea(5)) // 78.53...
console.log(MathUtils.clamp(15, 0, 10)) // 10
// const m = new MathUtils()
// m.circleArea(5) // Ошибка TS: Property 'circleArea' does not exist on instanceclass User {
static count = 0
static readonly maxUsers = 1000
readonly id: number
name: string
constructor(name: string) {
if (User.count >= User.maxUsers) {
throw new Error('Достигнут лимит пользователей')
}
User.count++
this.id = User.count
this.name = name
}
static reset() {
User.count = 0
}
}
new User('Алексей') // User.count = 1
new User('Ольга') // User.count = 2
console.log(User.count) // 2 — общее для всех экземпляровСтатические члены идеально подходят для паттерна Singleton — гарантируют, что существует ровно один экземпляр:
class Database {
private static instance: Database | null = null
private connected = false
private constructor(private url: string) {} // приватный конструктор!
static getInstance(url: string): Database {
if (!Database.instance) {
Database.instance = new Database(url)
}
return Database.instance
}
connect() {
if (!this.connected) {
console.log(`Подключение к ${this.url}`)
this.connected = true
}
}
}
const db1 = Database.getInstance('postgres://localhost:5432/mydb')
const db2 = Database.getInstance('postgres://other-host') // та же строка игнорируется
console.log(db1 === db2) // true — один и тот же экземплярTypeScript поддерживает статические блоки инициализации:
class Config {
static readonly defaults: Record<string, string>
static readonly version: string
static {
// Сложная инициализация статических полей
Config.defaults = { lang: 'ru', theme: 'light', timeout: '5000' }
Config.version = process.env.APP_VERSION ?? '1.0.0'
console.log('Config инициализирован')
}
}class Counter {
// Статические — общие для всех
static totalCreated = 0
static totalDestroyed = 0
// Экземплярные — у каждого свои
private count = 0
constructor() {
Counter.totalCreated++
}
increment() { this.count++ }
getValue() { return this.count }
destroy() { Counter.totalDestroyed++ }
static getActiveCount() {
return Counter.totalCreated - Counter.totalDestroyed
}
}
const c1 = new Counter()
const c2 = new Counter()
c1.increment()
c1.increment()
c2.increment()
console.log(c1.getValue()) // 2 — только у c1
console.log(c2.getValue()) // 1 — только у c2
console.log(Counter.totalCreated) // 2 — общееПаттерн Singleton и фабрика объектов с использованием статических полей: реестр соединений с БД
// Singleton через статические поля — один экземпляр на всё приложение
class ConnectionPool {
static #instance = null
static #defaultConfig = {
host: 'localhost',
port: 5432,
maxSize: 10,
timeout: 5000,
}
#connections = []
#config
#stats = { created: 0, released: 0, failed: 0 }
// Приватный конструктор — нельзя вызвать напрямую
constructor(config) {
this.#config = { ...ConnectionPool.#defaultConfig, ...config }
}
// Единственная точка доступа к экземпляру
static getInstance(config = {}) {
if (!ConnectionPool.#instance) {
ConnectionPool.#instance = new ConnectionPool(config)
console.log('[Pool] Создан новый пул соединений')
}
return ConnectionPool.#instance
}
static reset() {
ConnectionPool.#instance = null
console.log('[Pool] Пул сброшен')
}
// Получить соединение из пула
acquire() {
if (this.#connections.length < this.#config.maxSize) {
const conn = {
id: ++this.#stats.created,
host: this.#config.host,
port: this.#config.port,
createdAt: Date.now(),
busy: true,
}
this.#connections.push(conn)
console.log(`[Pool] Соединение #${conn.id} создано`)
return conn
}
throw new Error(`Пул заполнен (max: ${this.#config.maxSize})`)
}
// Вернуть соединение в пул
release(conn) {
const idx = this.#connections.findIndex(c => c.id === conn.id)
if (idx === -1) throw new Error('Соединение не принадлежит пулу')
this.#connections.splice(idx, 1)
this.#stats.released++
console.log(`[Pool] Соединение #${conn.id} освобождено`)
}
get stats() {
return {
...this.#stats,
active: this.#connections.length,
config: { ...this.#config },
}
}
}
// Registry — статический реестр через Map
class ServiceRegistry {
static #services = new Map()
static #count = 0
static register(name, factory) {
if (ServiceRegistry.#services.has(name)) {
throw new Error(`Сервис "${name}" уже зарегистрирован`)
}
ServiceRegistry.#services.set(name, { factory, count: 0 })
ServiceRegistry.#count++
console.log(`[Registry] Зарегистрирован: ${name}`)
}
static get(name) {
const entry = ServiceRegistry.#services.get(name)
if (!entry) throw new Error(`Сервис "${name}" не найден`)
entry.count++
return entry.factory()
}
static get registeredCount() { return ServiceRegistry.#count }
static list() { return [...ServiceRegistry.#services.keys()] }
}
// --- Демонстрация ---
console.log('=== Singleton: ConnectionPool ===')
const pool1 = ConnectionPool.getInstance({ host: 'db.prod.com', maxSize: 5 })
const pool2 = ConnectionPool.getInstance({ host: 'другой хост' }) // игнорируется
console.log('Это один экземпляр:', pool1 === pool2) // true
const conn1 = pool1.acquire()
const conn2 = pool1.acquire()
console.log('Активных соединений:', pool1.stats.active) // 2
pool1.release(conn1)
console.log('После release:', pool1.stats)
console.log('\n=== Статический реестр сервисов ===')
ServiceRegistry.register('logger', () => ({ log: console.log }))
ServiceRegistry.register('formatter', () => ({ format: (s) => s.toUpperCase() }))
console.log('Зарегистрировано:', ServiceRegistry.registeredCount) // 2
console.log('Список:', ServiceRegistry.list())
const logger = ServiceRegistry.get('logger')
logger.log('Сервис получен из реестра')
try {
ServiceRegistry.register('logger', () => {})
} catch (e) {
console.log('Ошибка:', e.message)
}Реализуй класс `IdGenerator` с приватным статическим счётчиком. Статический метод `generate(prefix)` — возвращает уникальный ID вида "prefix-1", "prefix-2" и т.д. (каждый вызов увеличивает счётчик). Статический метод `reset()` — сбрасывает счётчик в 0. Статический геттер `count` — возвращает текущее значение счётчика. Также реализуй паттерн Singleton: класс `AppConfig` с приватным конструктором, статическим методом `getInstance()` который возвращает один и тот же экземпляр, полем `settings` (объект) и методом `set(key, value)` для обновления настроек.
IdGenerator.generate: IdGenerator.#counter++; return `${prefix}-${IdGenerator.#counter}`. reset: IdGenerator.#counter = 0. AppConfig.getInstance: if (!AppConfig.#instance) AppConfig.#instance = new AppConfig(); return AppConfig.#instance.
Токены для AI-помощника закончились
Купи токены чтобы задавать вопросы AI прямо в уроке