¥
立即购买

JavaScript类声明生成器

9 浏览
1 试用
0 购买
Dec 8, 2025更新

本提示词专为JavaScript开发场景设计,能够根据用户需求生成符合最佳实践的类声明代码。通过精确的参数配置,可指定类名、继承关系、构造函数参数等关键要素,确保生成的代码结构清晰、语法规范。适用于Web开发、客户端脚本编写、交互式应用构建等多种业务场景,帮助开发者快速创建高质量的JavaScript类定义,提升开发效率和代码质量。

class ModalDialog extends BaseComponent {
  constructor(container, options = {}) {
    // 调用父类构造函数(如父类需要其他签名,可按需调整)
    super(container, options);

    // 支持传入选择器字符串或 DOM 元素
    const root =
      container instanceof Element
        ? container
        : typeof container === 'string'
          ? document.querySelector(container)
          : null;

    if (!root) {
      throw new TypeError('ModalDialog: "container" must be a DOM Element or a valid selector.');
    }

    // 合并默认配置与用户配置
    this.options = { ...ModalDialog.getDefaultOptions(), ...options };

    // 容器与内部节点引用
    this.container = root;
    this.nodes = {
      overlay: null,
      dialog: null,
      header: null,
      titleEl: null,
      content: null,
      closeBtn: null
    };

    // 状态与辅助字段
    this._id = this.options.id || `modal-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
    this._isRendered = false;
    this._isOpen = false;
    this._eventsBound = false;
    this._lastFocused = null;

    // 事件处理器引用(用于解绑)
    this._handlers = {
      onKeyDown: null,
      onOverlayClick: null,
      onCloseClick: null
    };

    if (this.options.autoRender) {
      this.render();
    }
  }

  // 渲染弹窗结构(只渲染一次)
  render() {
    if (this._isRendered) return this;

    // 创建遮罩层
    const overlay = document.createElement('div');
    overlay.className = 'modal-overlay';
    overlay.setAttribute('data-modal-overlay', '');
    // 避免被辅助技术聚焦
    overlay.setAttribute('aria-hidden', 'true');

    // 创建对话框
    const dialog = document.createElement('div');
    dialog.className = `modal-dialog${this.options.cssClass ? ` ${this.options.cssClass}` : ''}`;
    dialog.id = this._id;
    dialog.setAttribute('role', 'dialog');
    dialog.setAttribute('aria-modal', 'true');
    dialog.setAttribute('aria-hidden', 'true');
    // 让对话框可聚焦以便管理焦点
    dialog.setAttribute('tabindex', '-1');

    // 可选标题栏
    let header = null;
    let titleEl = null;
    if (this.options.title) {
      header = document.createElement('div');
      header.className = 'modal-header';

      titleEl = document.createElement('h2');
      titleEl.className = 'modal-title';
      titleEl.id = `${this._id}-title`;
      titleEl.textContent = this.options.title;

      header.appendChild(titleEl);
      dialog.setAttribute('aria-labelledby', titleEl.id);
    } else if (this.options.ariaLabel) {
      // 无标题时使用 aria-label
      dialog.setAttribute('aria-label', this.options.ariaLabel);
    }

    // 关闭按钮
    const closeBtn = document.createElement('button');
    closeBtn.className = 'modal-close';
    closeBtn.type = 'button';
    closeBtn.setAttribute('aria-label', this.options.closeLabel || 'Close');
    closeBtn.textContent = this.options.closeText || '×';

    // 内容容器
    const content = document.createElement('div');
    content.className = 'modal-content';

    // 渲染内容
    if (this.options.content instanceof Node) {
      content.appendChild(this.options.content);
    } else if (typeof this.options.content === 'function') {
      const result = this.options.content();
      if (result instanceof Node) {
        content.appendChild(result);
      } else if (typeof result === 'string') {
        if (this.options.allowHTML) {
          // 注意:开启 allowHTML 应确保内容已安全处理
          content.innerHTML = result;
        } else {
          content.textContent = result;
        }
      }
    } else if (typeof this.options.content === 'string') {
      if (this.options.allowHTML) {
        // 注意:开启 allowHTML 应确保内容已安全处理
        content.innerHTML = this.options.content;
      } else {
        content.textContent = this.options.content;
      }
    }

    // 组装节点
    if (!header) {
      // 若无 header,关闭按钮直接放置在对话框顶层
      dialog.appendChild(closeBtn);
    } else {
      header.appendChild(closeBtn);
      dialog.appendChild(header);
    }
    dialog.appendChild(content);
    overlay.appendChild(dialog);
    this.container.appendChild(overlay);

    // 保存引用
    this.nodes.overlay = overlay;
    this.nodes.dialog = dialog;
    this.nodes.header = header;
    this.nodes.titleEl = titleEl;
    this.nodes.content = content;
    this.nodes.closeBtn = closeBtn;

    this._isRendered = true;
    return this;
  }

  // 打开弹窗(管理可见性、ARIA、焦点与事件)
  open() {
    if (this._isOpen) return this;
    if (!this._isRendered) this.render();

    // 保存当前焦点以便关闭后恢复
    this._lastFocused = document.activeElement instanceof HTMLElement ? document.activeElement : null;

    // 可见性与状态
    this.nodes.dialog.setAttribute('aria-hidden', 'false');
    this.nodes.overlay.classList.add(this.options.transitionClass);
    this.nodes.dialog.classList.add(this.options.transitionClass);

    // 绑定事件
    this.bindEvents();

    // 聚焦策略
    if (this.options.trapFocus) {
      this._focusFirstFocusable();
    } else {
      this.nodes.dialog.focus();
    }

    this._isOpen = true;
    return this;
  }

  // 关闭弹窗(恢复焦点、解绑事件与可见性)
  close() {
    if (!this._isOpen) return this;

    // 可见性与状态
    this.nodes.dialog.setAttribute('aria-hidden', 'true');
    this.nodes.overlay.classList.remove(this.options.transitionClass);
    this.nodes.dialog.classList.remove(this.options.transitionClass);

    // 解绑事件
    this._unbindEvents();

    // 恢复之前的焦点
    if (this._lastFocused && typeof this._lastFocused.focus === 'function' && document.contains(this._lastFocused)) {
      this._lastFocused.focus();
    }
    this._lastFocused = null;

    this._isOpen = false;

    // 如果需要在关闭后销毁
    if (this.options.destroyOnClose) {
      this.destroy();
    }

    return this;
  }

  // 绑定事件(防重复绑定)
  bindEvents() {
    if (!this._isRendered || this._eventsBound) return this;

    const { overlay, dialog, closeBtn } = this.nodes;

    this._handlers.onKeyDown = (e) => {
      // ESC 关闭
      if (this.options.closeOnEsc && e.key === 'Escape') {
        e.preventDefault();
        this.close();
        return;
      }

      // 焦点陷阱
      if (this.options.trapFocus && e.key === 'Tab') {
        const focusables = this._getFocusableElements();
        if (focusables.length === 0) {
          // 无可聚焦元素时,保持对话框本身聚焦
          e.preventDefault();
          dialog.focus();
          return;
        }
        const currentIndex = focusables.indexOf(document.activeElement);
        let nextIndex = currentIndex;

        if (e.shiftKey) {
          nextIndex = currentIndex <= 0 ? focusables.length - 1 : currentIndex - 1;
        } else {
          nextIndex = currentIndex === focusables.length - 1 ? 0 : currentIndex + 1;
        }

        e.preventDefault();
        focusables[nextIndex].focus();
      }
    };

    this._handlers.onOverlayClick = (e) => {
      if (!this.options.closeOnBackdrop) return;
      // 仅当点击在遮罩层本身(而非对话框内部)时关闭
      if (e.target === overlay) {
        this.close();
      }
    };

    this._handlers.onCloseClick = () => {
      this.close();
    };

    document.addEventListener('keydown', this._handlers.onKeyDown, true);
    overlay.addEventListener('mousedown', this._handlers.onOverlayClick, true);
    closeBtn.addEventListener('click', this._handlers.onCloseClick, true);

    this._eventsBound = true;
    return this;
  }

  // 销毁实例(移除 DOM 与事件)
  destroy() {
    // 若还打开,先关闭以便处理解绑
    if (this._isOpen) {
      this.close();
    } else {
      // 确保事件已解绑
      this._unbindEvents();
    }

    if (this._isRendered && this.nodes.overlay && this.nodes.overlay.parentNode) {
      this.nodes.overlay.parentNode.removeChild(this.nodes.overlay);
    }

    // 清空引用
    this.nodes = {
      overlay: null,
      dialog: null,
      header: null,
      titleEl: null,
      content: null,
      closeBtn: null
    };
    this._isRendered = false;
    return this;
  }

  // 静态方法:默认配置
  static getDefaultOptions() {
    return {
      id: null,
      title: '',
      ariaLabel: '',
      content: '',
      allowHTML: false, // true 时请确保内容已安全处理
      cssClass: '',
      closeLabel: 'Close',
      closeText: '×',
      closeOnEsc: true,
      closeOnBackdrop: true,
      trapFocus: true,
      autoRender: true,
      destroyOnClose: false,
      transitionClass: 'is-open'
    };
  }

  // 获取对话框内部可聚焦元素列表
  _getFocusableElements() {
    if (!this.nodes.dialog) return [];
    const selector = [
      'a[href]',
      'area[href]',
      'button:not([disabled])',
      'input:not([disabled]):not([type="hidden"])',
      'select:not([disabled])',
      'textarea:not([disabled])',
      'iframe',
      'audio[controls]',
      'video[controls]',
      '[contenteditable]',
      '[tabindex]:not([tabindex="-1"])'
    ].join(',');

    const all = Array.from(this.nodes.dialog.querySelectorAll(selector));
    // 过滤不可见元素
    return all.filter((el) => {
      const style = window.getComputedStyle(el);
      return style.visibility !== 'hidden' && style.display !== 'none' && el.offsetParent !== null;
    });
  }

  // 聚焦第一个可聚焦元素,若不存在则聚焦对话框
  _focusFirstFocusable() {
    const focusables = this._getFocusableElements();
    if (focusables.length > 0) {
      focusables[0].focus();
    } else {
      this.nodes.dialog.focus();
    }
  }

  // 解绑事件(内部使用)
  _unbindEvents() {
    if (!this._eventsBound) return;

    const { overlay, closeBtn } = this.nodes;

    document.removeEventListener('keydown', this._handlers.onKeyDown, true);
    if (overlay) overlay.removeEventListener('mousedown', this._handlers.onOverlayClick, true);
    if (closeBtn) closeBtn.removeEventListener('click', this._handlers.onCloseClick, true);

    this._eventsBound = false;
  }
}
// Assumes EventEmitter is available in scope (e.g., Node.js: const { EventEmitter } = require('events');)
class UserProfileModel extends EventEmitter {
  /**
   * @param {object} service - Data service with load(id), save(profile), update(id, patch) async methods.
   * @param {number} cacheTTL - Cache time-to-live in milliseconds (default: 5 minutes).
   */
  constructor(service, cacheTTL = 300000) {
    super();

    if (!service || typeof service !== 'object') {
      throw new TypeError('UserProfileModel: "service" must be a non-null object with load/save/update methods.');
    }
    if (typeof cacheTTL !== 'number' || !Number.isFinite(cacheTTL) || cacheTTL < 0) {
      throw new TypeError('UserProfileModel: "cacheTTL" must be a non-negative finite number.');
    }

    this.#service = service;
    this.#cacheTTL = cacheTTL;
    this.#data = null;        // Cached user profile object
    this.#userId = null;      // Last loaded/saved user identifier
    this.#cachedAt = 0;       // Timestamp of last cache update (ms)
  }

  // -------------------------
  // Instance methods
  // -------------------------

  /**
   * Load a user profile by id with TTL-based caching.
   * @param {string|number} userId - User identifier.
   * @param {{ force?: boolean }} [options] - If force=true, bypass cache.
   * @returns {Promise<object|null>} Deep-cloned user profile or null if none.
   */
  async load(userId, { force = false } = {}) {
    const effectiveId = userId ?? this.#userId;
    if (effectiveId == null) {
      throw new Error('UserProfileModel.load: "userId" is required on first load.');
    }

    // Serve from cache if fresh and not forced
    if (
      !force &&
      this.#data &&
      (this.#userId === effectiveId) &&
      (Date.now() - this.#cachedAt < this.#cacheTTL)
    ) {
      return this.toJSON();
    }

    if (typeof this.#service.load !== 'function') {
      throw new Error('UserProfileModel.load: service.load(id) is not implemented.');
    }

    const profile = await this.#service.load(effectiveId);
    if (!profile || typeof profile !== 'object') {
      throw new Error('UserProfileModel.load: service.load must resolve to an object.');
    }

    this.validate(profile);
    this.#userId = profile.id ?? effectiveId;
    this.#commit(profile);

    return this.toJSON();
  }

  /**
   * Persist a full user profile.
   * @param {object} profile - Full profile object to save.
   * @returns {Promise<object>} Deep-cloned saved profile.
   */
  async save(profile) {
    if (!profile || typeof profile !== 'object') {
      throw new TypeError('UserProfileModel.save: "profile" must be a non-null object.');
    }
    if (typeof this.#service.save !== 'function') {
      throw new Error('UserProfileModel.save: service.save(profile) is not implemented.');
    }

    this.validate(profile);
    const saved = await this.#service.save(profile);
    const result = (saved && typeof saved === 'object') ? saved : profile;

    this.#userId = result.id ?? this.#userId ?? profile.id ?? this.#userId;
    this.#commit(result);

    return this.toJSON();
  }

  /**
   * Apply partial updates to the current profile and persist.
   * @param {object} patch - Partial fields to update.
   * @returns {Promise<object>} Deep-cloned updated profile.
   */
  async update(patch) {
    if (!patch || typeof patch !== 'object') {
      throw new TypeError('UserProfileModel.update: "patch" must be a non-null object.');
    }
    const currentId = this.#userId ?? this.#data?.id;
    if (currentId == null) {
      throw new Error('UserProfileModel.update: No profile loaded/saved yet. Call load/save first.');
    }
    if (typeof this.#service.update !== 'function') {
      throw new Error('UserProfileModel.update: service.update(id, patch) is not implemented.');
    }

    // Validate the resulting object before persisting
    const next = { ...(this.#data ?? {}), ...patch };
    this.validate(next);

    const updated = await this.#service.update(currentId, patch);
    const result = (updated && typeof updated === 'object') ? updated : next;

    this.#commit(result);
    return this.toJSON();
  }

  /**
   * Return a deep-cloned snapshot of the current profile.
   * @returns {object|null}
   */
  toJSON() {
    if (this.#data == null) return null;
    return UserProfileModel.clone(this.#data);
  }

  /**
   * Validate a profile object. Throws on invalid data.
   * - id: optional string|number
   * - name: optional non-empty string
   * - email: optional valid email format
   * - createdAt/updatedAt: optional Date or ISO date string
   * @param {object} [data] - Profile to validate; defaults to current.
   * @returns {true} Returns true if valid.
   */
  validate(data) {
    const d = data ?? this.#data;
    if (!d || typeof d !== 'object') {
      throw new Error('UserProfileModel.validate: No profile data to validate.');
    }

    // id: optional string or number
    if ('id' in d && d.id != null && typeof d.id !== 'string' && typeof d.id !== 'number') {
      throw new TypeError('UserProfileModel.validate: "id" must be a string or number when provided.');
    }

    // name: optional non-empty string
    if ('name' in d && d.name != null) {
      if (typeof d.name !== 'string' || d.name.trim().length === 0) {
        throw new TypeError('UserProfileModel.validate: "name" must be a non-empty string when provided.');
      }
    }

    // email: optional valid format
    if ('email' in d && d.email != null) {
      const email = String(d.email).trim();
      const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
      if (!emailRegex.test(email)) {
        throw new TypeError('UserProfileModel.validate: "email" must be a valid email address when provided.');
      }
    }

    // createdAt / updatedAt: optional Date or ISO-8601 string
    const isValidDateValue = (v) => (v instanceof Date && !isNaN(v)) || (!isNaN(Date.parse(v)));
    if ('createdAt' in d && d.createdAt != null && !isValidDateValue(d.createdAt)) {
      throw new TypeError('UserProfileModel.validate: "createdAt" must be a Date or ISO date string when provided.');
    }
    if ('updatedAt' in d && d.updatedAt != null && !isValidDateValue(d.updatedAt)) {
      throw new TypeError('UserProfileModel.validate: "updatedAt" must be a Date or ISO date string when provided.');
    }

    return true;
  }

  /**
   * Subscribe to change events. Returns an unsubscribe function.
   * @param {(payload: { previous: object|null, current: object|null }) => void} handler
   * @returns {() => void} Unsubscribe function
   */
  onChange(handler) {
    if (typeof handler !== 'function') {
      throw new TypeError('UserProfileModel.onChange: "handler" must be a function.');
    }
    this.on('change', handler);
    return () => {
      if (typeof this.off === 'function') {
        this.off('change', handler);
      } else if (typeof this.removeListener === 'function') {
        this.removeListener('change', handler);
      }
    };
  }

  // -------------------------
  // Private state and helpers
  // -------------------------

  // Service adapter instance
  #service;
  // Cache controls
  #cacheTTL;
  #cachedAt;
  // Profile state
  #data;
  #userId;

  /**
   * Commit new data into the model, refresh cache timestamp, and emit change event.
   * @param {object} newData
   * @private
   */
  #commit(newData) {
    const previous = this.#data ? UserProfileModel.clone(this.#data) : null;
    // Store a cloned snapshot to avoid external mutations affecting internal state
    this.#data = UserProfileModel.clone(newData);
    this.#cachedAt = Date.now();

    // Emit a cloned payload to ensure immutability to subscribers
    this.emit('change', {
      previous,
      current: UserProfileModel.clone(this.#data),
    });
  }

  // -------------------------
  // Static helpers
  // -------------------------

  /**
   * Deep clone utility. Uses structuredClone when available, falls back to JSON.
   * @param {any} value
   * @returns {any}
   */
  static clone(value) {
    if (typeof structuredClone === 'function') {
      return structuredClone(value);
    }
    // Note: JSON fallback does not preserve Dates/undefined/functions.
    return JSON.parse(JSON.stringify(value));
  }
}

示例详情

解决的问题

  • 用最少的输入,极速生成可直接粘贴的 JavaScript 类声明,覆盖组件、业务模型、工具库与典型设计模式等高频场景。
  • 精准控制类名、继承、构造参数、方法清单等关键要素,确保结构清晰、职责明确、易扩展。
  • 自动给出规范注释与合理组织方式,减少低级错误与返工,缩短评审时间。
  • 统一团队风格与最佳实践,作为“规范化生成器”,提升协作效率与代码一致性。
  • 让新人上手更快、资深开发更省时,满足前端、全栈、脚本开发在不同项目中的快速落地与复用需求。

适用用户

前端开发工程师

快速搭建组件类与数据模型;一键生成构造函数与方法骨架;减少样板代码,专注交互与体验。

全栈/Node.js工程师

迅速生成服务端模块类与工具类;按需配置继承与静态方法;统一风格,缩短从设计到上线的周期。

技术负责人/架构师

沉淀团队类模板标准;约束命名与结构;用可复制的输出推动代码评审通过与跨项目复用。

特征总结

一键生成符合规范的JavaScript类声明,按需配置类名、继承与方法,快速搭好可复用骨架。
自动补全构造函数与必要注释,清晰标注关键逻辑,团队成员上手即懂、协作更顺畅。
内置最佳实践风格与命名建议,默认输出可读性强的结构,减少返工与代码评审时间。
支持多场景模板化生成,如组件类、业务模型、工具类,按场景自动调整结构与职责。
灵活参数化控制属性与方法签名,避免手写重复样板,帮助你聚焦核心业务实现。
自动校验语法与基本逻辑,提交前自查错误与隐患,降低线上风险与回滚成本。
可选静态方法与继承链设计建议,规范扩展路径,确保后续维护与二次开发一致。
无缝融入团队代码风格,统一缩进与注释规则,轻松通过代码检查与合并流程。
支持迭代式生成与优化,基于反馈快速重构类结构,持续提升可读性与可维护性。

如何使用购买的提示词模板

1. 直接在外部 Chat 应用中使用

将模板生成的提示词复制粘贴到您常用的 Chat 应用(如 ChatGPT、Claude 等),即可直接对话使用,无需额外开发。适合个人快速体验和轻量使用场景。

2. 发布为 API 接口调用

把提示词模板转化为 API,您的程序可任意修改模板参数,通过接口直接调用,轻松实现自动化与批量处理。适合开发者集成与业务系统嵌入。

3. 在 MCP Client 中配置使用

在 MCP client 中配置对应的 server 地址,让您的 AI 应用自动调用提示词模板。适合高级用户和团队协作,让提示词在不同 AI 工具间无缝衔接。

AI 提示词价格
¥20.00元
先用后买,用好了再付款,超安全!

您购买后可以获得什么

获得完整提示词模板
- 共 670 tokens
- 4 个可调节参数
{ 类名称 } { 继承父类 } { 构造函数参数 } { 需要的方法 }
获得社区贡献内容的使用权
- 精选社区优质案例,助您快速上手提示词
使用提示词兑换券,低至 ¥ 9.9
了解兑换券 →
限时半价

不要错过!

半价获取高级提示词-优惠即将到期

17
:
23
小时
:
59
分钟
:
59