¥
立即购买

JavaScript对象生成器

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

本提示词专为JavaScript开发场景设计,能够根据用户指定的属性自动生成符合最佳实践的对象结构。通过精确的参数化输入,确保生成的代码具备技术准确性、结构清晰性和规范性。支持多种对象类型和属性配置,适用于前端开发、数据建模和教学演示等场景,帮助开发者快速构建高质量的JavaScript对象代码。

/**
 * Blog Post object toolkit for CMS forms, preview, and local-storage sync.
 * - ES2020+ compatible, browser-first.
 * - Provides: factory, validation, update helpers, preview, and draft storage.
 */

/**
 * @typedef {Object} BlogPost
 * @property {string} title        // 文章标题
 * @property {string} slug         // URL 友好的标识符(根据标题生成,或手动设置)
 * @property {string} content      // 正文(纯文本/Markdown/HTML)
 * @property {string} summary      // 摘要(若为空则从 content 推导)
 * @property {string[]} tags       // 标签(去重、裁剪空白)
 * @property {string} authorName   // 作者显示名
 * @property {string} authorId     // 作者唯一标识
 * @property {string} coverUrl     // 封面图 URL
 * @property {boolean} published   // 是否发布
 * @property {string} createdAt    // ISO 8601 创建时间
 * @property {string} updatedAt    // ISO 8601 更新时间
 */

// -------------------- Utilities --------------------

const toISO = (date = new Date()) => new Date(date).toISOString();

/**
 * Create a URL-friendly slug from title or input.
 * - lowercases, trims, removes diacritics, keeps a-z0-9 and hyphens.
 */
function slugify(input, maxLength = 80) {
  const s = String(input ?? '')
    .trim()
    .toLowerCase()
    .normalize('NFKD')              // split diacritics
    .replace(/[\u0300-\u036f]/g, '')// remove diacritics marks
    .replace(/[^a-z0-9]+/g, '-')    // non-alnum => hyphen
    .replace(/^-+|-+$/g, '')        // trim hyphens
    .slice(0, maxLength);
  return s || 'untitled';
}

/**
 * Strip HTML to plain text (safe in browser using DOMParser; falls back to regex).
 */
function stripHTML(html) {
  if (typeof window !== 'undefined' && typeof DOMParser !== 'undefined') {
    const doc = new DOMParser().parseFromString(String(html ?? ''), 'text/html');
    return doc.body.textContent || '';
  }
  // Fallback (non-browser)
  return String(html ?? '').replace(/<[^>]*>/g, ' ');
}

/**
 * Derive a summary from summary/content with max length and ellipsis.
 */
function deriveSummary({ summary, content }, maxLen = 160) {
  if (typeof summary === 'string' && summary.trim()) return summary.trim();
  const text = stripHTML(content).replace(/\s+/g, ' ').trim();
  if (!text) return '';
  return text.length > maxLen ? text.slice(0, maxLen - 1).trimEnd() + '…' : text;
}

/**
 * Normalize tags: trim, remove empty, de-duplicate, optional limit.
 */
function normalizeTags(tags = [], max = 50) {
  const out = [];
  const seen = new Set();
  for (const t of tags) {
    if (typeof t !== 'string') continue;
    const v = t.trim();
    if (!v || seen.has(v)) continue;
    out.push(v);
    seen.add(v);
    if (out.length >= max) break;
  }
  return out;
}

/**
 * Quick word count-based reading time (optional helper for UI).
 */
function estimateReadingTime(text, wordsPerMin = 250) {
  const words = String(stripHTML(text)).trim().split(/\s+/).filter(Boolean).length;
  const mins = Math.max(1, Math.ceil(words / Math.max(100, wordsPerMin)));
  return { minutes: mins, words };
}

// -------------------- Factory & Validation --------------------

/**
 * Create a new BlogPost with sane defaults and normalization.
 * @param {Partial<BlogPost>} initial
 * @returns {BlogPost}
 */
function createBlogPost(initial = {}) {
  const now = toISO();

  const title = String(initial.title ?? '').trim();
  const createdAt = (typeof initial.createdAt === 'string' && !Number.isNaN(Date.parse(initial.createdAt)))
    ? new Date(initial.createdAt).toISOString()
    : now;

  const content = String(initial.content ?? '');
  const post = {
    title,
    slug: slugify(initial.slug || title),
    content,
    summary: deriveSummary({ summary: initial.summary, content }),
    tags: normalizeTags(initial.tags),
    authorName: String(initial.authorName ?? ''),
    authorId: String(initial.authorId ?? ''),
    coverUrl: String(initial.coverUrl ?? ''),
    published: Boolean(initial.published),
    createdAt,
    updatedAt: now,
  };

  return post;
}

/**
 * Validate a BlogPost object. Returns {valid, errors[]}
 * - Type checks, ISO date checks, basic slug rules.
 */
function validateBlogPost(post) {
  const errors = [];

  const isString = (v) => typeof v === 'string';
  const mustString = ['title', 'slug', 'content', 'summary', 'authorName', 'authorId', 'coverUrl', 'createdAt', 'updatedAt'];

  for (const k of mustString) {
    if (!isString(post[k])) errors.push(`"${k}" must be a string`);
  }

  if (!Array.isArray(post.tags)) errors.push('"tags" must be an array');
  else if (!post.tags.every((t) => typeof t === 'string')) errors.push('"tags" must be string[]');

  if (typeof post.published !== 'boolean') errors.push('"published" must be a boolean');

  const isoFields = ['createdAt', 'updatedAt'];
  for (const f of isoFields) {
    if (isString(post[f]) && Number.isNaN(Date.parse(post[f]))) {
      errors.push(`"${f}" must be a valid ISO 8601 date string`);
    }
  }

  // basic slug sanity
  if (isString(post.slug) && !/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(post.slug)) {
    errors.push('"slug" should be lower-case alphanumerics separated by hyphens');
  }

  return { valid: errors.length === 0, errors };
}

/**
 * Functional update: returns a new BlogPost with normalized fields and updatedAt bumped.
 * - If title is changed and slug not explicitly provided, regenerate slug from title.
 * - Summary is rederived when summary not provided or empty string.
 * - Tags are normalized.
 */
function updateBlogPost(post, patch = {}) {
  const merged = { ...post, ...patch };

  // Handle slug regeneration rules
  const titleChanged = typeof patch.title === 'string' && patch.title !== post.title;
  const slugProvided = Object.prototype.hasOwnProperty.call(patch, 'slug');
  if (titleChanged && !slugProvided) {
    merged.slug = slugify(merged.title);
  } else if (slugProvided) {
    merged.slug = slugify(patch.slug);
  }

  // Normalize tags and summary
  merged.tags = normalizeTags(patch.tags ?? post.tags);
  merged.summary = deriveSummary({ summary: merged.summary, content: merged.content });

  // Bump updatedAt
  merged.updatedAt = toISO();

  return merged;
}

// -------------------- Serialization & Storage --------------------

/**
 * Serialize BlogPost to JSON string (stable keys for readability).
 */
function serializeBlogPost(post) {
  const ordered = {
    title: post.title,
    slug: post.slug,
    content: post.content,
    summary: post.summary,
    tags: post.tags,
    authorName: post.authorName,
    authorId: post.authorId,
    coverUrl: post.coverUrl,
    published: post.published,
    createdAt: post.createdAt,
    updatedAt: post.updatedAt,
  };
  return JSON.stringify(ordered);
}

/**
 * Parse JSON or plain object into a normalized BlogPost.
 */
function parseBlogPost(input) {
  const obj = typeof input === 'string' ? JSON.parse(input) : input;
  return createBlogPost(obj);
}

/**
 * Generate a stable draft storage key for localStorage.
 */
function draftKey(post) {
  return `cms:draft:${post.slug || 'untitled'}`;
}

/**
 * Save draft to localStorage. Returns the storage key or null on failure.
 */
function saveDraft(post, key = draftKey(post)) {
  try {
    localStorage.setItem(key, serializeBlogPost(post));
    return key;
  } catch (err) {
    // Optionally surface error to caller
    return null;
  }
}

/**
 * Load draft from localStorage. Returns BlogPost or null if not found/invalid.
 */
function loadDraft(key) {
  try {
    const raw = localStorage.getItem(key);
    if (!raw) return null;
    return parseBlogPost(raw);
  } catch {
    return null;
  }
}

/**
 * Remove draft from localStorage. Returns true if removed.
 */
function clearDraft(key) {
  try {
    localStorage.removeItem(key);
    return true;
  } catch {
    return false;
  }
}

// -------------------- Preview Helper --------------------

/**
 * Build a lightweight preview DTO for UI rendering (no HTML).
 */
function getPreview(post, maxSummaryLen = 160) {
  return {
    title: post.title || '未命名',
    slug: post.slug,
    summary: deriveSummary({ summary: post.summary, content: post.content }, maxSummaryLen),
    coverUrl: post.coverUrl || '',
    tags: post.tags,
    published: post.published,
    updatedAt: post.updatedAt,
    readingTime: estimateReadingTime(post.content).minutes,
  };
}

// Example export (if using modules)
// export {
//   createBlogPost,
//   updateBlogPost,
//   validateBlogPost,
//   serializeBlogPost,
//   parseBlogPost,
//   saveDraft,
//   loadDraft,
//   clearDraft,
//   getPreview,
// };

对象结构说明

  • 采用工厂函数创建对象,提供可靠的默认值与规范化处理,便于在表单中直接绑定。
  • 提供函数式更新方法 updateBlogPost,避免副作用并自动维护 updatedAt。
  • 内置 slug 生成、摘要推导、标签归一化、ISO 时间戳、草稿本地存储支持。
  • 提供 getPreview 生成轻量预览数据用于卡片/列表展示。

属性详细说明表 | 属性名 | 类型 | 必填 | 默认值 | 说明 | |---|---|---:|---|---| | title | string | 否 | '' | 文章标题;用于生成 slug | | slug | string | 否 | 由 title 推导或 'untitled' | URL 标识,lowercase-hyphen | | content | string | 否 | '' | 正文(纯文本/Markdown/HTML) | | summary | string | 否 | 从 content 推导 | 摘要;若提供空串,将重新推导 | | tags | string[] | 否 | [] | 标签,自动去重与裁剪空白 | | authorName | string | 否 | '' | 作者显示名 | | authorId | string | 否 | '' | 作者唯一标识(建议必填) | | coverUrl | string | 否 | '' | 封面图 URL | | published | boolean | 否 | false | 是否发布 | | createdAt | string(ISO) | 否 | 创建时刻 | ISO 8601 时间字符串 | | updatedAt | string(ISO) | 否 | 创建/更新时刻 | ISO 8601 时间字符串 |

使用示例代码

// 1) 一键生成表单初始对象
let post = createBlogPost({
  title: '用现代 JavaScript 写出更好的代码',
  authorName: 'Jane Doe',
  authorId: 'user_123',
  tags: ['JavaScript', 'Web', '  JavaScript  '], // 会去重 -> ['JavaScript', 'Web']
});
console.log(post.slug); // yong-xian-dai-javascript-xie-chu-geng-hao-de-dai-ma(示例)

// 2) 表单编辑:内容或标题变化时进行函数式更新
post = updateBlogPost(post, { content: '# 正文内容\n更多文本...' });
// 标题变化时,若未手动提供 slug,将自动重算 slug
post = updateBlogPost(post, { title: '新标题' });

// 3) 校验(用于提交前)
const { valid, errors } = validateBlogPost(post);
if (!valid) {
  console.error('校验失败:', errors);
}

// 4) 预览卡片数据(摘要、阅读时长)
const preview = getPreview(post);
console.log(preview);

// 5) 本地存储草稿
const key = saveDraft(post);    // cms:draft:<slug>
const loaded = loadDraft(key);  // 读取并自动规范化
clearDraft(key);                // 清除草稿

最佳实践建议

  • Slug 稳定性:文章发布后建议固定 slug,不要因标题变更自动更新,以避免链接失效。可在 UI 上提供“锁定 slug”的开关。
  • 安全渲染:content 若包含 HTML/Markdown,请在渲染时使用可靠库进行解析与消毒(如 DOMPurify),切勿直接注入未消毒的 HTML。
  • ISO 时间:持久化与接口交互统一使用 ISO 8601 字符串,便于跨时区与后端协作。
  • 函数式更新:通过 updateBlogPost 返回新对象,便于在状态管理库(如 React/Redux/Zustand)中追踪变更。
  • 标签规范:保持标签数组小而精(如 ≤ 50 个),避免大小写或前后空白导致的重复。
  • 校验扩展:根据业务规则可扩展 validateBlogPost(例如要求 authorId 必填、coverUrl 合法性校验等)。
  • 本地存储命名空间:使用明确前缀(如 cms:draft:)避免与其他存储键冲突,并考虑配合用户 ID 做多账号隔离。
/**
 * ChatMessage —— 聊天消息对象模型(前端友好,安全高亮提及,支持字数统计)
 * 特性:
 * - 不可变对象(Object.freeze),避免无意修改
 * - 内置字数统计(wordCount)
 * - 提及高亮的安全分段(segmentsForRender),不返回可注入的 HTML
 * - withUpdates(patch) 以不可变方式更新对象(变更 content 时自动将 edited=true,除非显式覆盖)
 */

/**
 * @typedef {Object} ChatMessageProps
 * @property {string} id
 * @property {string} conversationId
 * @property {string} role
 * @property {string} content
 * @property {number} tokens
 * @property {string[]} mentions
 * @property {string} createdAt   ISO 8601 时间字符串
 * @property {boolean} edited
 * @property {string[]} reactions
 * @property {string} replyToId
 */

const ChatMessage = (() => {
  // ---------- utilities ----------
  const isISODateTime = (s) => typeof s === 'string' && !Number.isNaN(Date.parse(s));
  const toISO = (dateLike) => {
    if (typeof dateLike === 'string' && isISODateTime(dateLike)) return new Date(dateLike).toISOString();
    if (dateLike instanceof Date) return dateLike.toISOString();
    return new Date().toISOString();
  };
  const isStringArray = (arr) => Array.isArray(arr) && arr.every((x) => typeof x === 'string');
  const clampNonNegInt = (n) => {
    const num = Number(n);
    return Number.isFinite(num) && num >= 0 ? Math.floor(num) : 0;
  };
  const escapeRegExp = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');

  const wordCountOf = (text) => {
    // 尽量用 Intl.Segmenter 更精确地统计词数
    if (typeof Intl !== 'undefined' && Intl.Segmenter) {
      const seg = new Intl.Segmenter(undefined, { granularity: 'word' });
      let c = 0;
      for (const { isWordLike } of seg.segment(text)) {
        if (isWordLike) c += 1;
      }
      return c;
    }
    // 回退:基于 Unicode 字母/数字的简单匹配
    const m = text.trim().match(/\p{L}[\p{L}\p{N}_-]*/gu);
    return m ? m.length : 0;
  };

  // 将 content 分割成 [{type:'text'|'mention', text: string, value?: string}]
  // 其中 value 为提及用户名/ID(不包含 @)
  const segmentMentions = (content, mentions) => {
    if (!mentions?.length) return [{ type: 'text', text: content }];
    const escaped = mentions.filter(Boolean).map(escapeRegExp);
    if (!escaped.length) return [{ type: 'text', text: content }];

    // 匹配 @alice @bob ...,在词边界/空白/行尾结束
    const re = new RegExp(`@(?:${escaped.join('|')})(?=\\b|\\s|$)`, 'gu');
    const out = [];
    let lastIndex = 0;
    let m;

    while ((m = re.exec(content))) {
      const start = m.index;
      const end = re.lastIndex;
      if (start > lastIndex) out.push({ type: 'text', text: content.slice(lastIndex, start) });
      out.push({ type: 'mention', text: m[0], value: m[0].slice(1) });
      lastIndex = end;
    }
    if (lastIndex < content.length) out.push({ type: 'text', text: content.slice(lastIndex) });
    return out;
  };

  class ChatMessage {
    /**
     * @param {Partial<ChatMessageProps> & Pick<ChatMessageProps,'id'|'conversationId'|'role'|'content'>} props
     */
    constructor({
      id,
      conversationId,
      role,
      content,
      tokens = 0,
      mentions = [],
      createdAt = new Date().toISOString(),
      edited = false,
      reactions = [],
      replyToId = '',
    }) {
      // 校验
      if (typeof id !== 'string' || id.trim() === '') throw new TypeError('id must be a non-empty string');
      if (typeof conversationId !== 'string' || conversationId.trim() === '') throw new TypeError('conversationId must be a non-empty string');
      if (typeof role !== 'string' || role.trim() === '') throw new TypeError('role must be a non-empty string');
      if (typeof content !== 'string') throw new TypeError('content must be a string');
      if (!isStringArray(mentions)) throw new TypeError('mentions must be a string[]');
      if (!isStringArray(reactions)) throw new TypeError('reactions must be a string[]');
      if (typeof edited !== 'boolean') throw new TypeError('edited must be boolean');
      if (typeof replyToId !== 'string') throw new TypeError('replyToId must be a string');

      // 赋值(只读风格)
      this.id = id;
      this.conversationId = conversationId;
      this.role = role;
      this.content = content;
      this.tokens = clampNonNegInt(tokens);
      this.mentions = mentions;
      this.createdAt = toISO(createdAt);
      this.edited = edited;
      this.reactions = reactions;
      this.replyToId = replyToId;

      Object.freeze(this);
    }

    // 词数统计(与 tokens 概念区分;tokens 通常由上游模型/服务计算)
    get wordCount() {
      return wordCountOf(this.content);
    }

    get hasMentions() {
      return Array.isArray(this.mentions) && this.mentions.length > 0;
    }

    // 用于渲染的安全分段。不返回 HTML,交给视图层做转义/拼接。
    segmentsForRender() {
      return segmentMentions(this.content, this.mentions);
    }

    // 不可变更新:返回新对象;若修改了 content 且未显式提供 edited,则自动设为 true
    withUpdates(patch = {}) {
      const merged = { ...this, ...patch };
      if ('content' in patch && !('edited' in patch)) {
        merged.edited = true;
      }
      return new ChatMessage(merged);
    }

    // 序列化为可传输的普通对象
    toJSON() {
      return {
        id: this.id,
        conversationId: this.conversationId,
        role: this.role,
        content: this.content,
        tokens: this.tokens,
        mentions: [...this.mentions],
        createdAt: this.createdAt,
        edited: this.edited,
        reactions: [...this.reactions],
        replyToId: this.replyToId,
      };
    }

    // 工厂方法(语义上等同于 new ChatMessage)
    static create(props) {
      return new ChatMessage(props);
    }

    // 轻量校验
    static isValid(obj) {
      try {
        // eslint-disable-next-line no-new
        new ChatMessage(obj);
        return true;
      } catch {
        return false;
      }
    }
  }

  return ChatMessage;
})();

export { ChatMessage };

对象结构说明

  • 类型:聊天消息对象,用于前端 IM 消息列表的渲染。
  • 设计理念:
    • 数据不可变,便于列表虚拟化、diff 对比与状态管理。
    • 将“提及高亮”实现为“安全分段”,避免使用 innerHTML 造成 XSS 风险。
    • tokens 字段独立于字数统计,便于接入上游模型/服务的 token 计数。

属性详细说明表 | 属性名 | 类型 | 必填 | 默认值 | 说明 | 示例 | |---|---|---|---|---|---| | id | string | 是 | - | 消息唯一 ID | "msg_001" | | conversationId | string | 是 | - | 会话 ID | "conv_123" | | role | string | 是 | - | 发送者角色,可按业务限定(如 "user"、"assistant"、"system") | "user" | | content | string | 是 | - | 消息内容 | "Hello @alice" | | tokens | number | 否 | 0 | 预计算 token 数(与字数不同),若无可设 0 | 17 | | mentions | string[] | 否 | [] | 需高亮的提及列表(不含前缀 @) | ["alice","bob"] | | createdAt | string | 否 | 当前时间 ISO | ISO 8601 时间字符串 | "2025-01-01T12:34:56.000Z" | | edited | boolean | 否 | false | 是否已编辑 | true | | reactions | string[] | 否 | [] | 表情/反应标识集合(如 emoji 或反应代号) | ["👍","❤️"] | | replyToId | string | 否 | "" | 被回复消息 ID,不存在则空字符串 | "msg_000" |

使用示例代码

import { ChatMessage } from './ChatMessage.js';

// 1) 创建消息对象
const msg = ChatMessage.create({
  id: 'msg_001',
  conversationId: 'conv_123',
  role: 'user',
  content: 'Hi @alice, welcome to the channel!',
  tokens: 9,
  mentions: ['alice'],
  createdAt: new Date().toISOString(),
  edited: false,
  reactions: ['👋'],
  replyToId: '',
});

// 2) 渲染:安全的提及高亮(Vanilla DOM 示例)
function renderMessage(container, message) {
  container.textContent = ''; // 清空
  const segments = message.segmentsForRender();
  segments.forEach((seg) => {
    const span = document.createElement('span');
    span.textContent = seg.text; // 使用 textContent,避免 XSS
    if (seg.type === 'mention') {
      span.className = 'mention'; // 样式类用于高亮
      // 也可以添加 dataset:span.dataset.mention = seg.value;
    }
    container.appendChild(span);
  });
}

// 3) 渲染:React(JSX)示例
// const segments = msg.segmentsForRender();
// return (
//   <p>
//     {segments.map((seg, i) =>
//       seg.type === 'mention' ? (
//         <mark className="mention" key={`${msg.id}-m-${i}`}>{seg.text}</mark>
//       ) : (
//         <span key={`${msg.id}-t-${i}`}>{seg.text}</span>
//       )
//     )}
//   </p>
// );

// 4) 统计字数(wordCount 与 tokens 概念不同)
console.log('wordCount:', msg.wordCount); // e.g., 6
console.log('tokens:', msg.tokens);       // 9

// 5) 更新消息(不可变):修改 content 时,edited 将自动为 true(除非显式传入)
const msgEdited = msg.withUpdates({
  content: 'Hi @alice and @bob!',
  mentions: ['alice', 'bob'],
});
console.log(msgEdited.edited); // true

// 6) 序列化传输
const payload = JSON.stringify(msgEdited); // 通过网络发送
const plainObject = JSON.parse(payload);
const restored = ChatMessage.create(plainObject); // 恢复为模型对象

最佳实践建议

  • 不可变数据:冻结实例并通过 withUpdates 返回新对象,便于状态追踪、撤销/重做与时间旅行调试。
  • 安全渲染:提及高亮使用分段数据结构,避免使用 innerHTML;在视图层使用 textContent 或框架默认转义。
  • 时间格式:统一使用 ISO 8601 字符串,减少时区误差;后端/存储也应保持一致。
  • 角色与提及:明确业务约定(role 的取值范围、mentions 是用户 ID 还是用户名),确保 content 中 @与 mentions 同步。
  • tokens 与字数分离:tokens 通常来自外部计数逻辑(如模型 tokenizer),不要用字数替代;必要时在入库/入列前填充。
  • 渲染性能:长列表配合虚拟列表(如 react-window);将 segmentsForRender 的结果做缓存(按 id+content+mentions 的键)。
  • 反应规范:统一 reaction 值(例如仅允许已知 emoji/枚举值),避免无限扩展导致 UI 不一致。

对象定义代码块

'use strict';

/**
 * 提示模板对象(PromptTemplate)
 * 使用 JSDoc 提供类型提示和 IDE 智能补全,便于教学演示与团队协作。
 *
 * @typedef {Object} PromptTemplate
 * @property {string} id                 - 模板唯一标识(如:"pt-2025-001")
 * @property {string} name               - 模板名称(人类可读)
 * @property {string} prompt             - 用户可见/可编辑的主提示词正文
 * @property {string} systemInstruction  - 系统角色指令(约束风格/边界/输出格式)
 * @property {number} temperature        - 采样温度,数值越高越有创造性(通常 0–2)
 * @property {number} maxTokens          - 输出最大 token 数
 * @property {string[]} stopWords        - 生成终止词列表(出现即截断)
 * @property {string} stylePreset        - 风格预设(如:"neutral"|"concise"|"formal"|"casual"|"technical" 等)
 * @property {string[]} examples         - 示例集合(少样本提示,便于教学对比)
 * @property {string} version            - 模板版本(如:"1.0.0")
 * @property {string} updatedAt          - ISO 8601 更新时间(如:"2025-12-01T10:20:30.000Z")
 */

/** 内部工具:深冻结(含数组) */
function deepFreeze(obj) {
  if (obj && typeof obj === 'object' && !Object.isFrozen(obj)) {
    Object.freeze(obj);
    Object.getOwnPropertyNames(obj).forEach((prop) => {
      const value = obj[prop];
      if (value && typeof value === 'object') deepFreeze(value);
    });
  }
  return obj;
}

/** 基础校验与规范化工具 */
function assertNonEmptyString(value, name) {
  if (typeof value !== 'string') {
    throw new TypeError(`${name} must be a string`);
  }
  const v = value.trim();
  if (!v) throw new TypeError(`${name} must be a non-empty string`);
  return v;
}

function assertNumber(value, name) {
  if (typeof value !== 'number' || Number.isNaN(value)) {
    throw new TypeError(`${name} must be a valid number`);
  }
  return value;
}

function assertPositiveInt(value, name) {
  const n = Number(value);
  if (!Number.isFinite(n) || n <= 0 || !Number.isInteger(n)) {
    throw new TypeError(`${name} must be a positive integer`);
  }
  return n;
}

function normalizeStringArray(value, name) {
  if (value == null) return [];
  if (!Array.isArray(value)) {
    throw new TypeError(`${name} must be an array of strings`);
  }
  const out = Array.from(
    new Set(
      value
        .map((v) => {
          if (typeof v !== 'string') {
            throw new TypeError(`${name} must contain only strings`);
          }
          return v.trim();
        })
        .filter(Boolean)
    )
  );
  return out;
}

function assertTemperature(value) {
  const n = assertNumber(value, 'temperature');
  if (n < 0 || n > 2) {
    throw new RangeError('temperature must be in [0, 2]');
  }
  return n;
}

function assertISO8601(value, name) {
  const v = assertNonEmptyString(value, name);
  // 粗略校验:Date 可解析且等价于自身的 ISO 字符串
  const d = new Date(v);
  if (Number.isNaN(d.getTime())) {
    throw new TypeError(`${name} must be a valid ISO 8601 string`);
  }
  return d.toISOString();
}

/**
 * 创建提示模板对象的工厂方法
 * - 进行必要的校验和规范化
 * - 补全合理默认值
 * - 返回不可变对象(deep freeze)
 *
 * @param {Partial<PromptTemplate> & Pick<PromptTemplate, 'id'|'name'|'prompt'|'systemInstruction'>} input
 * @returns {PromptTemplate}
 */
function createPromptTemplate(input) {
  if (!input || typeof input !== 'object') {
    throw new TypeError('input must be an object');
  }

  // 必填字段
  const id = assertNonEmptyString(input.id, 'id');
  const name = assertNonEmptyString(input.name, 'name');
  const prompt = assertNonEmptyString(input.prompt, 'prompt');
  const systemInstruction = assertNonEmptyString(input.systemInstruction, 'systemInstruction');

  // 可选字段 + 默认值
  const temperature = input.temperature == null ? 0.7 : assertTemperature(input.temperature);
  const maxTokens = input.maxTokens == null ? 512 : assertPositiveInt(input.maxTokens, 'maxTokens');
  const stopWords = normalizeStringArray(input.stopWords, 'stopWords');
  const examples = normalizeStringArray(input.examples, 'examples');

  const stylePreset =
    input.stylePreset == null
      ? 'neutral'
      : assertNonEmptyString(input.stylePreset, 'stylePreset');

  const version =
    input.version == null ? '1.0.0' : assertNonEmptyString(input.version, 'version');

  const updatedAt =
    input.updatedAt == null
      ? new Date().toISOString()
      : assertISO8601(input.updatedAt, 'updatedAt');

  /** @type {PromptTemplate} */
  const template = {
    id,
    name,
    prompt,
    systemInstruction,
    temperature,
    maxTokens,
    stopWords,
    stylePreset,
    examples,
    version,
    updatedAt,
  };

  return deepFreeze(template);
}

/**
 * 类型守卫(运行时形状检查的轻量版本)
 * @param {any} obj
 * @returns {obj is PromptTemplate}
 */
function isPromptTemplate(obj) {
  const has = (k) => Object.prototype.hasOwnProperty.call(obj, k);
  return (
    obj &&
    typeof obj === 'object' &&
    typeof obj.id === 'string' &&
    typeof obj.name === 'string' &&
    typeof obj.prompt === 'string' &&
    typeof obj.systemInstruction === 'string' &&
    typeof obj.temperature === 'number' &&
    typeof obj.maxTokens === 'number' &&
    Array.isArray(obj.stopWords) &&
    typeof obj.stylePreset === 'string' &&
    Array.isArray(obj.examples) &&
    typeof obj.version === 'string' &&
    typeof obj.updatedAt === 'string' &&
    has('id') &&
    has('name') &&
    has('prompt') &&
    has('systemInstruction')
  );
}

// 可按需 export:
// export { createPromptTemplate, isPromptTemplate };

对象结构说明

  • 该对象用于“教学演示中快速固化提示模板”,统一参数与风格,便于生成对比示例文案。
  • 采用工厂函数创建并深度冻结,确保模板不可变,减少演示中的意外修改。
  • 提供基础校验与规范化(去空格、去重、范围校验、ISO 时间),保证参数一致性。
  • 通过 stylePreset、temperature 与 examples 等属性,可方便地做 A/B 对比与风格切换演示。

属性详细说明表

属性名 类型 必填 默认值 说明 约束/示例
id string 模板唯一标识 例:pt-2025-001
name string 模板名称 例:教学-电商标题模板
prompt string 主提示词正文 可包含占位符(如:{{topic}})
systemInstruction string 系统角色指令,定义边界/风格 建议简洁明确、包含输出格式
temperature number 0.7 创造性与确定性的权衡 [0, 2]
maxTokens number(int) 512 最大输出 tokens 正整数
stopWords string[] [] 生成停止词 去重、去空格
stylePreset string "neutral" 风格预设标签 例:"concise"、"formal"、"casual"、"technical"
examples string[] [] 少样本示例 教学用对比数据
version string "1.0.0" 模板版本 语义化版本
updatedAt string(ISO) 当前时间 更新时间 ISO 8601,如 2025-12-01T10:20:30.000Z

使用示例代码

// 1) 创建两个风格不同的模板,便于教学对比
const neutralTemplate = createPromptTemplate({
  id: 'pt-2025-001',
  name: '教学-电商标题(中性)',
  systemInstruction: '你是一位资深电商文案助手,请确保标题简洁、无夸张、不含违规词,输出以一行纯文本为主。',
  prompt: '请为以下产品生成 5 个电商商品标题:\n产品信息:{{product}}\n目标人群:{{audience}}',
  temperature: 0.7,
  maxTokens: 256,
  stylePreset: 'neutral',
  stopWords: ['END'],
  examples: [
    '产品:轻薄办公笔记本;人群:大学生',
    '产品:降噪蓝牙耳机;人群:通勤族',
  ],
  version: '1.1.0',
});

const conciseTemplate = createPromptTemplate({
  id: 'pt-2025-002',
  name: '教学-电商标题(简洁)',
  systemInstruction: '你是一位极简风格的电商文案助手,仅输出高密度信息点,避免形容词堆砌。',
  prompt: '基于信息生成 5 个高转化标题:\n信息:{{product}}|人群:{{audience}}',
  temperature: 0.5,
  maxTokens: 200,
  stylePreset: 'concise',
  stopWords: ['END'],
  examples: [
    '产品:轻薄办公笔记本;人群:大学生',
    '产品:降噪蓝牙耳机;人群:通勤族',
  ],
  version: '1.1.0',
});

// 2) 渲染函数(演示用):将变量插入 prompt,并拼合系统指令
function composePrompt(template, vars) {
  const inject = (text) =>
    text.replace(/{{\s*(\w+)\s*}}/g, (_, k) => (vars?.[k] ?? `{{${k}}}`));

  const header = `[Style: ${template.stylePreset}] [Temp: ${template.temperature}] [Max: ${template.maxTokens}]`;
  return `${template.systemInstruction}\n${header}\n\n${inject(template.prompt)}`;
}

// 3) 展示对比示例文案(教学用)
function renderComparison(templateA, templateB, exampleVars) {
  const a = composePrompt(templateA, exampleVars);
  const b = composePrompt(templateB, exampleVars);
  return `【模板 A:${templateA.name}】\n${a}\n\n———\n\n【模板 B:${templateB.name}】\n${b}`;
}

const exampleVars = {
  product: '轻薄办公笔记本(i5/16GB/512GB,1.2kg)',
  audience: '大学新生',
};

console.log(renderComparison(neutralTemplate, conciseTemplate, exampleVars));

// 4) 使用 examples 做批量演示(少样本对比)
function batchFromExamples(template) {
  return template.examples.map((ex, idx) => {
    // 简单解析示例中的“产品”“人群”,仅教学示范(真实项目请使用更健壮的解析)
    const product = ex.match(/产品:[;;]?\s*([^;;]+)\s*/)?.[1] ?? '';
    const audience = ex.match(/人群:[;;]?\s*([^;;]+)\s*/)?.[1] ?? '';
    return `【示例 ${idx + 1}】\n${composePrompt(template, { product, audience })}`;
  });
}

console.log(batchFromExamples(neutralTemplate).join('\n\n'));

最佳实践建议

  • 明确分离数据与呈现:模板对象只承载参数与文案片段,渲染与变量注入放在独立的纯函数中,便于测试与复用。
  • 保持不可变:创建后使用深冻结,保证教学时参数不被意外篡改,形成“可复现的演示环境”。
  • 严格校验与规范化:对字符串去空格、数组去重、数值与范围校验、时间统一用 ISO 8601,保持团队口径一致。
  • 风格预设统一:建议在团队内定义有限集合的 stylePreset(如 neutral/concise/formal/casual/technical),降低认知成本。
  • 版本与更新时间:version 使用语义化版本;updatedAt 统一自动写入 ISO 字符串,便于审计与回滚。
  • 示例驱动教学:examples 列表专注少量高质量样本,便于对比不同模板输出;示例语料保持结构稳定,方便解析与演示。
  • 安全与合规:stopWords 仅用于模型输出截断;不要在模板中硬编码敏感信息;避免使用已废弃 API 或隐式全局变量。
  • 可扩展性:如需多语言/多渠道,可在不破坏当前结构的前提下,以外部映射表管理差异(例如不同平台的输出格式)。

示例详情

解决的问题

面向前端开发、技术负责人与教学培训,快速把“属性需求”转化为标准化的 JavaScript 对象方案:1) 一步生成结构清晰、易维护的对象代码;2) 自动给出对象结构说明、属性清单与使用示例,减少沟通与文档编写成本;3) 内建最佳实践与规范建议,帮助团队统一风格、避免过时与不良写法;4) 针对不同场景(开发建模、重构优化、教学演示)自动调整设计,提升交付速度与代码质量,降低返工与缺陷率;5) 支持多种对象创建方式的智能选择,兼顾可读性与性能,让新人上手更快、老手更省时。

适用用户

前端开发工程师

在开发页面状态与组件数据时,快速生成对象、类或构造函数,实现字段默认值、只读与校验设置;一键生成示例与注释,加速联调与提测。

Node.js后端/全栈工程师

为服务配置、业务实体与响应封装快速建模,按场景切换对象创建方式;在重构旧模块时批量替换不规范对象,降低线上故障率。

技术负责人/团队负责人

制定统一对象创建规范与命名策略,沉淀为团队模版;用自动注释与示例提升代码审阅效率,缩短新人上手周期。

特征总结

按业务字段一键生成可直接粘贴的对象代码,结构清晰,命名规范,减少手写与返工。
支持对象字面量、构造函数与类等多风格,一键切换生成方式,匹配团队代码习惯。
自动识别属性类型与默认值,生成合理校验与只读设置,降低运行时错误与维护成本。
内置注释与使用示例,复制即可运行,便于新人上手与团队共享,减少沟通说明时间。
根据不同场景智能优化对象设计,适配前端状态、后端返回数据映射与表单校验需求。
一键输出最佳实践建议与注意事项,提前规避废弃语法与安全隐患,提升代码可靠性。
可按团队约定定制命名、注释与风格参数,统一对象创建规范,提升协作与代码审阅效率。
教学演示友好,逐步展示结构与意图,帮助讲解面向对象与现代语法,提升课堂互动感。

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

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

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

2. 发布为 API 接口调用

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

3. 在 MCP Client 中配置使用

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

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

您购买后可以获得什么

获得完整提示词模板
- 共 498 tokens
- 3 个可调节参数
{ 对象类型 } { 属性定义 } { 使用场景 }
获得社区贡献内容的使用权
- 精选社区优质案例,助您快速上手提示词
使用提示词兑换券,低至 ¥ 9.9
了解兑换券 →
限时半价

不要错过!

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

17
:
23
小时
:
59
分钟
:
59