← Назад к вопросам

Как работать с классом динамически создаваемым внутри функции?

2.2 Middle🔥 111 комментариев
#JavaScript Core

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI3 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Как работать с классом динамически создаваемым внутри функции

Динамическое создание классов — продвинутый паттерн JavaScript. Это позволяет создавать классы на лету, использовать замыкания для инкапсуляции, и решать сложные архитектурные задачи.

Базовое создание класса внутри функции

function createUser() {
  // Класс определяется внутри функции
  class User {
    constructor(name, email) {
      this.name = name;
      this.email = email;
    }
    
    greet() {
      return `Привет, я ${this.name}`;
    }
  }
  
  // Возвращаем класс из функции
  return User;
}

const UserClass = createUser();
const user = new UserClass('Иван', 'ivan@example.com');
console.log(user.greet()); // 'Привет, я Иван'

Важный момент: каждый вызов функции создаёт новый класс, даже если они имеют одинаковую структуру.

Классы с замыканиями

Динамически создаваемые классы могут использовать переменные из замыкания:

function createCounter(startValue = 0) {
  let count = startValue; // Переменная замыкания
  
  class Counter {
    increment() {
      return ++count;
    }
    
    decrement() {
      return --count;
    }
    
    getValue() {
      return count;
    }
    
    reset() {
      count = startValue;
    }
  }
  
  return Counter;
}

const Counter1 = createCounter(0);
const Counter2 = createCounter(100);

const c1 = new Counter1();
const c2 = new Counter2();

console.log(c1.increment()); // 1
console.log(c2.increment()); // 101
console.log(c1.getValue());  // 1
console.log(c2.getValue());  // 101

Каждый экземпляр имеет свой независимый счётчик благодаря замыканию.

Параметризованные классы

function createValidator(rules) {
  class FormValidator {
    constructor(data) {
      this.data = data;
      this.errors = [];
    }
    
    validate() {
      this.errors = [];
      
      for (const [field, rule] of Object.entries(rules)) {
        const value = this.data[field];
        
        if (!rule(value)) {
          this.errors.push(`Поле ${field} не прошло валидацию`);
        }
      }
      
      return this.errors.length === 0;
    }
    
    getErrors() {
      return this.errors;
    }
  }
  
  return FormValidator;
}

// Создаём специфичный валидатор для пользователей
const UserValidator = createValidator({
  email: (v) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(v),
  password: (v) => v && v.length >= 8,
  name: (v) => v && v.length >= 2
});

const validator = new UserValidator({
  email: 'test@example.com',
  password: 'short',
  name: 'Jo'
});

const isValid = validator.validate();
console.log(isValid);           // false
console.log(validator.getErrors()); // Массив ошибок

Наследование с динамическими классами

function createBase(config) {
  class Base {
    constructor(data) {
      this.data = data;
      this.config = config;
    }
    
    save() {
      console.log(`Сохраняю ${this.config.name}:`, this.data);
    }
    
    delete() {
      console.log(`Удаляю ${this.config.name}`);
    }
  }
  
  return Base;
}

function createUserModel() {
  const BaseModel = createBase({ name: 'User' });
  
  class UserModel extends BaseModel {
    getDisplayName() {
      return `${this.data.firstName} ${this.data.lastName}`;
    }
    
    isAdmin() {
      return this.data.role === 'admin';
    }
  }
  
  return UserModel;
}

const User = createUserModel();
const user = new User({ firstName: 'Иван', lastName: 'Петров', role: 'user' });

user.save();                 // Сохраняю User: {...}
console.log(user.getDisplayName()); // 'Иван Петров'
console.log(user.isAdmin()); // false

Паттерн Factory с динамическими классами

const entityFactory = (() => {
  const entities = {};
  
  return {
    register(name, schema) {
      // Создаём класс для каждой сущности
      class Entity {
        constructor(data) {
          this.validate(data);
          Object.assign(this, data);
        }
        
        validate(data) {
          for (const field of Object.keys(schema)) {
            if (!(field in data)) {
              throw new Error(`Поле ${field} обязательно`);
            }
          }
        }
        
        toJSON() {
          const result = {};
          for (const field of Object.keys(schema)) {
            result[field] = this[field];
          }
          return result;
        }
      }
      
      // Добавляем имя для отладки
      Object.defineProperty(Entity, 'name', { value: name });
      
      entities[name] = Entity;
      return Entity;
    },
    
    create(name, data) {
      if (!(name in entities)) {
        throw new Error(`Сущность ${name} не зарегистрирована`);
      }
      return new entities[name](data);
    }
  };
})();

// Регистрируем сущности
entityFactory.register('Product', {
  id: 'number',
  name: 'string',
  price: 'number'
});

entityFactory.register('User', {
  id: 'number',
  email: 'string',
  username: 'string'
});

// Используем
const product = entityFactory.create('Product', {
  id: 1,
  name: 'Ноутбук',
  price: 50000
});

const user = entityFactory.create('User', {
  id: 1,
  email: 'test@example.com',
  username: 'john'
});

console.log(product.toJSON());
console.log(user.toJSON());

Динамические методы и свойства

function createAPIClient(endpoints) {
  class APIClient {
    constructor(baseURL) {
      this.baseURL = baseURL;
      
      // Создаём метод для каждого endpoint
      for (const [name, config] of Object.entries(endpoints)) {
        this[name] = async (params = {}) => {
          const url = `${this.baseURL}${config.path}`;
          const method = config.method || 'GET';
          
          const response = await fetch(url, {
            method,
            body: method !== 'GET' ? JSON.stringify(params) : undefined
          });
          
          return response.json();
        };
      }
    }
  }
  
  return APIClient;
}

const Client = createAPIClient({
  getUsers: { path: '/users', method: 'GET' },
  createUser: { path: '/users', method: 'POST' },
  deleteUser: { path: '/users/:id', method: 'DELETE' }
});

const client = new Client('https://api.example.com');
await client.getUsers();      // GET /users
await client.createUser({ name: 'John' }); // POST /users

Рекомендации

  • Используйте динамические классы когда нужна инкапсуляция через замыкания
  • Factory паттерн подходит для создания похожих классов по конфигурации
  • Помните о производительности — создание классов имеет overhead
  • Для массивного создания объектов - один класс лучше чем множество динамических
  • Отладка может быть сложнее — дайте классам правильные имена через Object.defineProperty
Как работать с классом динамически создаваемым внутри функции? | PrepBro