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

Как динамически создать класс в runtime?

1.6 Junior🔥 101 комментариев
#JavaScript Core

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

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

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

Как динамически создать класс в runtime

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

1. Динамическое создание через Function конструктор

// Способ 1: Function конструктор
const DynamicClass = new Function(
  'name',
  `
    this.name = name;
    this.greet = function() {
      return 'Hello, ' + this.name;
    }
  `
);

const user = new DynamicClass('Alice');
console.log(user.name); // 'Alice'
console.log(user.greet()); // 'Hello, Alice'

Проблемы:

  • Очень небезопасно (eval-подобно)
  • Плохо читается
  • Нет типизации в TypeScript
  • Slow по производительности

Когда использовать: Практически никогда. Есть лучшие способы.

2. Factory функция — безопаснее и проще

// Создание класса-фабрики
function createUserClass(defaultRole = 'user') {
  class User {
    constructor(name, email) {
      this.name = name;
      this.email = email;
      this.role = defaultRole;
    }

    greet() {
      return `Hello, I am ${this.name}`;
    }

    getInfo() {
      return `${this.name} (${this.role})`;
    }
  }

  return User;
}

const AdminUser = createUserClass('admin');
const RegularUser = createUserClass('user');

const admin = new AdminUser('Alice', 'alice@example.com');
const regular = new RegularUser('Bob', 'bob@example.com');

console.log(admin.getInfo()); // 'Alice (admin)'
console.log(regular.getInfo()); // 'Bob (user)'

Плюсы:

  • Безопасно
  • Читаемо
  • Работает с TypeScript
  • Гибко

Это лучший способ в 99% случаев.

3. Метапрограммирование через Proxy

// Создание класса с динамическим поведением
const DynamicClass = new Proxy(
  class {},
  {
    construct(target, args) {
      // Динамически создаём объект во время создания экземпляра
      const [name, age] = args;
      return {
        name,
        age,
        greet() {
          return `Hello, ${this.name}`;
        }
      };
    }
  }
);

const user = new DynamicClass('Alice', 30);
console.log(user.greet()); // 'Hello, Alice'

Когда использовать: Когда нужно перехватить создание экземпляра и изменить его поведение.

4. Динамическое добавление методов

function createClass(methodNames) {
  class DynamicClass {
    constructor(data) {
      this.data = data;
    }
  }

  // Добавляем методы динамически
  methodNames.forEach(methodName => {
    DynamicClass.prototype[methodName] = function() {
      return `Executing ${methodName} on ${this.data}`;
    };
  });

  return DynamicClass;
}

const MyClass = createClass(['run', 'walk', 'jump']);
const instance = new MyClass('rabbit');

console.log(instance.run()); // 'Executing run on rabbit'
console.log(instance.walk()); // 'Executing walk on rabbit'
console.log(instance.jump()); // 'Executing jump on rabbit'

5. Создание класса с наследованием

function createDerivedClass(ParentClass, additionalMethods) {
  class DynamicChild extends ParentClass {
    constructor(...args) {
      super(...args);
      this.dynamicProp = 'dynamic';
    }
  }

  // Добавляем методы
  Object.entries(additionalMethods).forEach(([name, method]) => {
    DynamicChild.prototype[name] = method;
  });

  return DynamicChild;
}

class Animal {
  constructor(name) {
    this.name = name;
  }

  speak() {
    return 'Some sound';
  }
}

const Dog = createDerivedClass(Animal, {
  bark() {
    return `${this.name} barks: woof!`;
  },
  wag() {
    return `${this.name} wags tail`;
  }
});

const dog = new Dog('Buddy');
console.log(dog.speak()); // 'Some sound'
console.log(dog.bark()); // 'Buddy barks: woof!'
console.log(dog.wag()); // 'Buddy wags tail'

6. Практический пример: регистр компонентов

// Создание динамических компонентов на базе конфигурации
function createComponentFactory(config) {
  class DynamicComponent {
    constructor(props = {}) {
      this.props = props;
      this.state = config.initialState || {};
    }

    render() {
      return config.render(this.props, this.state);
    }

    setState(newState) {
      this.state = { ...this.state, ...newState };
    }
  }

  // Добавляем методы жизненного цикла
  if (config.onMount) {
    DynamicComponent.prototype.onMount = config.onMount;
  }
  if (config.onDestroy) {
    DynamicComponent.prototype.onDestroy = config.onDestroy;
  }

  return DynamicComponent;
}

const ButtonComponent = createComponentFactory({
  initialState: { clicks: 0 },
  render(props, state) {
    return `<button>${props.label} (${state.clicks})</button>`;
  },
  onMount() {
    console.log('Button mounted');
  }
});

const btn = new ButtonComponent({ label: 'Click me' });
console.log(btn.render()); // '<button>Click me (0)</button>'
btn.setState({ clicks: 1 });
console.log(btn.render()); // '<button>Click me (1)</button>'

7. С использованием eval (НЕ РЕКОМЕНДУЕТСЯ!)

// ОПАСНО! Только для демонстрации
function createClassWithEval(className, methodCode) {
  const code = `
    class ${className} {
      ${methodCode}
    }
  `;
  eval(code);
  return eval(className);
}

// НЕ ДЕЛАЙ ТАК! eval очень небезопасен

8. Практический пример: ORM-подобный паттерн

function createModel(config) {
  class Model {
    constructor(data) {
      Object.assign(this, data);
    }

    save() {
      return { ...this, saved: true };
    }

    validate() {
      return config.validators.every(v => v(this));
    }

    static find(query) {
      // Имитация поиска
      return `Found ${JSON.stringify(query)}`;
    }
  }

  // Добавляем валидаторы
  config.validators = config.validators || [];

  return Model;
}

const User = createModel({
  validators: [
    (user) => typeof user.name === 'string',
    (user) => typeof user.email === 'string'
  ]
});

const user = new User({ name: 'Alice', email: 'alice@example.com' });
console.log(user.validate()); // true
console.log(user.save()); // { name: 'Alice', email: '...', saved: true }
console.log(User.find({ role: 'admin' })); // 'Found {...}'

Сравнение подходов

СпособБезопасностьПроизводительностьЧитаемостьКогда использовать
Function конструкторНизкаяМедленноПлохаяПочти никогда
Factory функцияВысокаяБыстроОтличнаяВ 99% случаев
ProxyСредняяБыстроСредняяПерехват поведения
Динамические методыВысокаяБыстроХорошаяКогда методы конфигурируемы
evalОЧЕНЬ низкаяМедленноУжаснаяНИКОГДА

TypeScript версия

function createUserClass<T extends Record<string, any>>(
  defaultProps: T
): new (name: string) => { name: string } & T {
  class User {
    name: string;
    
    constructor(name: string) {
      this.name = name;
      Object.assign(this, defaultProps);
    }
  }

  return User as any;
}

const User = createUserClass({ role: 'user' });
const user = new User('Alice');

Ключевой вывод

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

  1. Factory функция (лучший выбор в 99% случаев)

    function createClass(...args) {
      class DynamicClass { ... }
      return DynamicClass;
    }
    
  2. Proxy (когда нужно перехватить поведение)

  3. Динамическое добавление методов (когда методы конфигурируемы)

Избегай Function конструктора и eval — они небезопасны и медленны. Factory функции — это современный, читаемый и безопасный способ.