热门角色不仅是灵感来源,更是你的效率助手。通过精挑细选的角色提示词,你可以快速生成高质量内容、提升创作灵感,并找到最契合你需求的解决方案。让创作更轻松,让价值更直接!
我们根据不同用户需求,持续更新角色库,让你总能找到合适的灵感入口。
本提示词专为JavaScript开发场景设计,能够根据用户需求生成符合最佳实践的JavaScript函数声明代码。通过精确的参数配置,可指定函数名称、功能描述、参数列表和返回值类型,确保生成的代码结构清晰、语法规范且具备完整的注释说明。适用于Web开发、客户端脚本编写、交互式应用构建等多种前端开发场景,帮助开发者快速创建高质量的JavaScript函数代码。
/**
* 将数值金额格式化为本地化的货币或千分位数字字符串。
* - 使用 Intl.NumberFormat 实现本地化格式化与千分位分隔。
* - 支持自定义货币代码、小数最小/最大小数位数。
* - 严格处理边界:NaN/Infinity/非法参数会抛出错误;规范化 -0 为 0。
*
* 适用场景:
* - 价格展示(页面 UI)
* - 报表导出(稳定的小数位与千分位格式)
*
* @param {number|string} amount - 要格式化的金额;支持 number 或可被 Number() 正确解析的字符串。
* @param {string|string[]} [locale] - BCP-47 语言标记(如 "zh-CN"、"en-US"),或其数组;省略时使用运行环境默认区域。
* @param {string} [currency] - 3 位 ISO 4217 货币代码(如 "CNY"、"USD")。省略则输出为普通带千分位的十进制数字。
* @param {number} [minFraction] - 最小小数位(0-20)。未传时使用该区域/货币的默认值。
* @param {number} [maxFraction] - 最大小数位(0-20),需不小于 minFraction。未传时使用该区域/货币的默认值。
* @returns {string} 已格式化的字符串。
*
* @throws {TypeError} 当 amount 非有限数值(NaN/Infinity)或类型不合法时抛出。
* @throws {RangeError} 当 locale/currency 非法,或小数位参数越界/相互矛盾时抛出。
*/
function formatCurrency(amount, locale, currency, minFraction, maxFraction) {
// 将输入转换为有限数值
const toFiniteNumber = (val) => {
if (typeof val === 'number') {
return val;
}
if (typeof val === 'string') {
const n = Number(val.trim());
return n;
}
throw new TypeError('formatCurrency: "amount" must be a number or a numeric string.');
};
let num = toFiniteNumber(amount);
if (!Number.isFinite(num)) {
throw new TypeError('formatCurrency: "amount" must be a finite number (not NaN/Infinity).');
}
// 规范化 -0 为 0,避免显示 "-0.00" 等
if (Object.is(num, -0)) {
num = 0;
}
// 处理货币代码:允许未提供;若提供则必须是 3 位字母
let upperCurrency;
if (currency != null) {
if (typeof currency !== 'string') {
throw new TypeError('formatCurrency: "currency" must be a string ISO 4217 code, e.g., "USD".');
}
upperCurrency = currency.trim().toUpperCase();
if (!/^[A-Z]{3}$/.test(upperCurrency)) {
throw new RangeError('formatCurrency: "currency" must be a 3-letter ISO 4217 code (e.g., "CNY", "USD").');
}
}
// 先用默认分组与样式创建一个格式化器,以获取该区域/货币的默认小数位
const style = upperCurrency ? 'currency' : 'decimal';
let defaultsNf;
try {
defaultsNf = new Intl.NumberFormat(locale, {
style,
currency: upperCurrency,
});
} catch (err) {
throw new RangeError(`formatCurrency: Invalid locale or currency. ${err.message}`);
}
const { minimumFractionDigits: defMin, maximumFractionDigits: defMax } =
defaultsNf.resolvedOptions();
// 归一化小数位参数
const normalizeDigits = (name, value) => {
if (value === undefined || value === null) return undefined;
const n = Number(value);
if (!Number.isFinite(n) || !Number.isInteger(n)) {
throw new RangeError(`formatCurrency: "${name}" must be an integer between 0 and 20.`);
}
if (n < 0 || n > 20) {
throw new RangeError(`formatCurrency: "${name}" must be between 0 and 20.`);
}
return n;
};
const min = normalizeDigits('minFraction', minFraction);
const max = normalizeDigits('maxFraction', maxFraction);
let finalMin;
let finalMax;
if (min === undefined && max === undefined) {
finalMin = defMin;
finalMax = defMax;
} else if (min !== undefined && max === undefined) {
finalMin = min;
finalMax = Math.max(min, defMax);
} else if (min === undefined && max !== undefined) {
finalMax = max;
finalMin = Math.min(defMin, max);
} else {
if (min > max) {
throw new RangeError('formatCurrency: "minFraction" cannot be greater than "maxFraction".');
}
finalMin = min;
finalMax = max;
}
// 创建最终格式化器
let formatter;
try {
formatter = new Intl.NumberFormat(locale, {
style,
currency: upperCurrency,
useGrouping: true,
minimumFractionDigits: finalMin,
maximumFractionDigits: finalMax,
// currencyDisplay 默认 "symbol";如报表需要 "code",可在注意事项中参考调整
});
} catch (err) {
throw new RangeError(`formatCurrency: Failed to create formatter. ${err.message}`);
}
return formatter.format(num);
}
函数用途:
参数说明:
返回值:
使用示例:
/**
* 封装基于 fetch 的 JSON 请求,支持重试(指数退避+抖动)、超时控制与中止信号、状态码分类处理与错误包装。
* 调用端建议统一捕获并上报日志(参见使用示例)。
*
* @template T
* @param {string | URL} url - 请求地址
* @param {RequestInit} [options] - fetch 的原生请求配置(headers、method、body 等)
* @param {number} [retries=2] - 最大重试次数(不含首次请求),例如 2 表示最多发起 3 次请求
* @param {number} [backoffMs=300] - 指数退避的初始等待时长(毫秒)
* @param {number} [timeoutMs=10000] - 每次请求的超时时长(毫秒)
* @param {AbortSignal} [signal] - 外部中止信号(优先级高于内部超时)
* @returns {Promise<T>} - 解析后的 JSON 数据
*
* @example
* // 推荐:在调用端统一日志上报(示例使用控制台代替)
* (async () => {
* try {
* const data = await fetchJsonWithRetry('https://api.example.com/data', {
* method: 'GET',
* headers: { 'Accept': 'application/json' }
* }, 3, 400, 8000);
* console.log('SUCCESS', data);
* } catch (err) {
* // 统一错误上报入口(可接入监控/埋点系统)
* console.error('REQUEST_FAILED', {
* message: err?.message,
* name: err?.name,
* code: err?.code,
* status: err?.status,
* url: err?.url,
* method: err?.method,
* category: err?.category,
* isRetryable: err?.isRetryable,
* });
* }
* })();
*/
async function fetchJsonWithRetry(
url,
options = {},
retries = 2,
backoffMs = 300,
timeoutMs = 10_000,
signal
) {
// ---- 常量配置 ----
const MAX_BACKOFF_MS = 30_000;
const ERROR_BODY_LIMIT = 8_192; // 错误体读取大小上限(字节)
const DEFAULT_ACCEPT = 'application/json';
// ---- 参数校验 ----
if (typeof retries !== 'number' || retries < 0 || !Number.isFinite(retries)) {
throw new TypeError('retries must be a non-negative finite number');
}
if (typeof backoffMs !== 'number' || backoffMs < 0 || !Number.isFinite(backoffMs)) {
throw new TypeError('backoffMs must be a non-negative finite number');
}
if (typeof timeoutMs !== 'number' || timeoutMs <= 0 || !Number.isFinite(timeoutMs)) {
throw new TypeError('timeoutMs must be a positive finite number');
}
// ---- 工具函数与错误类型 ----
class RequestError extends Error {
/**
* @param {string} message
* @param {object} meta
* @param {string} [meta.code] - 错误码,如 ETIMEDOUT/EFETCH/HTTP_5XX/HTTP_4XX/ABORTED 等
* @param {boolean} [meta.isRetryable=false] - 是否建议重试
* @param {string | URL} [meta.url]
* @param {string} [meta.method]
* @param {number} [meta.status]
* @param {string} [meta.statusText]
* @param {string} [meta.category] - 'network' | 'timeout' | 'abort' | 'client' | 'server'
* @param {Record<string,string>} [meta.responseHeaders]
* @param {unknown} [meta.responseBodySnippet]
* @param {Error} [meta.cause]
*/
constructor(message, meta = {}) {
super(message);
this.name = 'RequestError';
this.code = meta.code;
this.isRetryable = Boolean(meta.isRetryable);
this.url = String(meta.url ?? '');
this.method = meta.method;
this.status = meta.status;
this.statusText = meta.statusText;
this.category = meta.category;
this.responseHeaders = meta.responseHeaders;
this.responseBodySnippet = meta.responseBodySnippet;
if (meta.cause) this.cause = meta.cause;
}
}
/** 解析 Retry-After 头为等待毫秒数(返回 null 表示不可用) */
function parseRetryAfterMs(hv) {
if (!hv) return null;
const seconds = Number(hv);
if (Number.isFinite(seconds)) {
return seconds > 0 ? seconds * 1000 : 0;
}
// 日期格式
const dateMs = Date.parse(hv);
if (Number.isFinite(dateMs)) {
const delay = dateMs - Date.now();
return delay > 0 ? delay : 0;
}
return null;
}
/** 指数退避 + 抖动(full jitter),并进行上限裁剪 */
function computeBackoffMs(base, attemptIndex) {
const exp = Math.pow(2, attemptIndex); // attemptIndex: 0,1,2,...
const raw = Math.min(base * exp, MAX_BACKOFF_MS);
return Math.floor(Math.random() * raw); // full jitter
}
/** 判断状态码是否可重试 */
function isRetryableStatus(status) {
if (status === 408) return true; // Request Timeout
if (status === 429) return true; // Too Many Requests
if (status >= 500 && status < 600) return true; // 5xx
return false;
}
/** 将 Headers 转为简单对象便于记录 */
function headersToObject(headers) {
/** @type {Record<string,string>} */
const obj = {};
headers.forEach((v, k) => { obj[k] = v; });
return obj;
}
/** 创建带超时和外部 signal 的联合信号 */
function createTimeoutLinkedSignal(timeout, externalSignal) {
const controller = new AbortController();
let timeoutId = null;
let timedOut = false;
const onExternalAbort = () => {
if (!controller.signal.aborted) {
controller.abort(externalSignal.reason ?? new DOMException('Aborted', 'AbortError'));
}
};
if (externalSignal) {
if (externalSignal.aborted) {
controller.abort(externalSignal.reason ?? new DOMException('Aborted', 'AbortError'));
} else {
externalSignal.addEventListener('abort', onExternalAbort, { once: true });
}
}
timeoutId = setTimeout(() => {
timedOut = true;
if (!controller.signal.aborted) {
// 兼容性:部分环境没有 DOMException,这里回退为 Error 并设置 name
const err = typeof DOMException !== 'undefined'
? new DOMException('TimeoutError', 'TimeoutError')
: Object.assign(new Error('TimeoutError'), { name: 'TimeoutError' });
controller.abort(err);
}
}, timeout);
const cleanup = () => {
if (timeoutId) clearTimeout(timeoutId);
if (externalSignal) externalSignal.removeEventListener('abort', onExternalAbort);
};
return { signal: controller.signal, cleanup, isTimedOut: () => timedOut };
}
/** 支持 signal 的 sleep,abort 时抛出 AbortError */
function sleep(ms, abortSignal) {
return new Promise((resolve, reject) => {
if (ms <= 0) return resolve();
let t = null;
const onAbort = () => {
if (t) clearTimeout(t);
const reason = abortSignal?.reason;
if (reason instanceof Error) return reject(reason);
const err = typeof DOMException !== 'undefined'
? new DOMException('Aborted', 'AbortError')
: Object.assign(new Error('Aborted'), { name: 'AbortError' });
reject(reason ?? err);
};
if (abortSignal?.aborted) return onAbort();
t = setTimeout(() => {
abortSignal?.removeEventListener('abort', onAbort);
resolve();
}, ms);
abortSignal?.addEventListener('abort', onAbort, { once: true });
});
}
/** 读取错误体(受大小上限约束),返回文本片段 */
async function readErrorBodySnippet(response) {
try {
const lenHeader = response.headers.get('content-length');
const len = lenHeader ? Number(lenHeader) : NaN;
if (Number.isFinite(len) && len > ERROR_BODY_LIMIT) {
return `[body omitted: content-length ${len} > limit ${ERROR_BODY_LIMIT}]`;
}
// 如果没有明确的 content-length,尽量读取,但截断
const text = await response.text();
return text.length > ERROR_BODY_LIMIT
? text.slice(0, ERROR_BODY_LIMIT) + '…[truncated]'
: text;
} catch {
return '[unreadable error body]';
}
}
/** 解析响应为 JSON(空体/204 返回 null) */
async function parseJsonSafely(response) {
if (response.status === 204) return null;
const ct = response.headers.get('content-type') || '';
// 某些接口返回 "" 或 text/plain 但实际是 JSON,尝试兼容
const isLikelyJson = /\bjson\b/i.test(ct) || ct === '';
if (!isLikelyJson) {
// 不是 JSON 类型也尝试 JSON.parse(若失败交给调用方感知)
const text = await response.text();
if (text === '') return null;
try {
return JSON.parse(text);
} catch (e) {
const err = new RequestError('Unexpected content-type, failed to parse JSON', {
code: 'EJSONPARSE',
isRetryable: false,
url,
method: options?.method || 'GET',
status: response.status,
statusText: response.statusText,
category: response.status >= 500 ? 'server' : 'client',
responseHeaders: headersToObject(response.headers),
responseBodySnippet: text.slice(0, ERROR_BODY_LIMIT),
cause: e instanceof Error ? e : undefined,
});
throw err;
}
}
// 标准 JSON 流程
if (response.headers.get('content-length') === '0') return null;
// 有些服务会返回空体但未设置 content-length=0
const text = await response.text();
if (text === '') return null;
return JSON.parse(text);
}
// ---- 准备请求头(不覆盖调用方显式传入) ----
const headers = new Headers(options.headers || {});
if (!headers.has('Accept')) headers.set('Accept', DEFAULT_ACCEPT);
// ---- 逐次尝试 ----
/** @type {Error | null} */
let lastError = null;
for (let attempt = 0; attempt <= retries; attempt++) {
const { signal: linkedSignal, cleanup, isTimedOut } = createTimeoutLinkedSignal(timeoutMs, signal);
try {
const res = await fetch(url, {
...options,
headers,
signal: linkedSignal,
});
if (res.ok) {
const data = await parseJsonSafely(res);
cleanup();
return /** @type {any} */ (data);
}
// 非 2xx,构造错误并判断是否重试
const status = res.status;
const category = status >= 500 ? 'server' : 'client';
const retryAfterMs = parseRetryAfterMs(res.headers.get('retry-after'));
const retryable = isRetryableStatus(status);
const bodySnippet = await readErrorBodySnippet(res);
const err = new RequestError(`HTTP ${status} ${res.statusText}`, {
code: status >= 500 ? 'HTTP_5XX' : status === 429 ? 'HTTP_429' : 'HTTP_4XX',
isRetryable: retryable,
url,
method: options?.method || 'GET',
status,
statusText: res.statusText,
category,
responseHeaders: headersToObject(res.headers),
responseBodySnippet: bodySnippet,
});
if (retryable && attempt < retries) {
const baseWait = retryAfterMs ?? computeBackoffMs(backoffMs, attempt);
// 避免负值/NaN
const waitMs = Math.max(0, Number.isFinite(baseWait) ? baseWait : backoffMs);
cleanup();
await sleep(waitMs, signal); // 等待期间尊重外部中止
lastError = err;
continue;
}
cleanup();
throw err;
} catch (e) {
cleanup();
// 外部中止:直接抛出
if (signal?.aborted) {
const err = new RequestError('Aborted by external signal', {
code: 'ABORTED',
isRetryable: false,
url,
method: options?.method || 'GET',
category: 'abort',
cause: e instanceof Error ? e : undefined,
});
throw err;
}
// 超时中止
if (e && (e.name === 'TimeoutError' || (e.name === 'AbortError' && isTimedOut()))) {
const timeoutErr = new RequestError('Request timed out', {
code: 'ETIMEDOUT',
isRetryable: attempt < retries, // 超时通常可重试
url,
method: options?.method || 'GET',
category: 'timeout',
cause: e instanceof Error ? e : undefined,
});
if (attempt < retries) {
const waitMs = computeBackoffMs(backoffMs, attempt);
await sleep(waitMs, signal);
lastError = timeoutErr;
continue;
}
throw timeoutErr;
}
// 其他网络类错误(如 DNS、连接失败等),视为可重试
const netErr = new RequestError('Network error during fetch', {
code: 'EFETCH',
isRetryable: attempt < retries,
url,
method: options?.method || 'GET',
category: 'network',
cause: e instanceof Error ? e : undefined,
});
if (attempt < retries) {
const waitMs = computeBackoffMs(backoffMs, attempt);
await sleep(waitMs, signal);
lastError = netErr;
continue;
}
throw netErr;
}
}
// 理论上不会到达此处
if (lastError) throw lastError;
throw new RequestError('Unknown error', { code: 'EUNKNOWN', url, method: options?.method || 'GET' });
}
/**
* @callback ScrollSpyOnChange
* @param {Object} detail - 变化详情
* @param {string|null} detail.activeId - 当前活动标题的 id(无匹配时为 null)
* @param {string|null} detail.previousId - 上一个活动标题的 id(首次计算或无匹配时为 null)
* @param {HTMLElement|null} detail.activeElement - 当前活动标题元素
* @param {number} detail.index - 当前活动标题在 sections 中的索引(无匹配时为 -1)
* @param {Array<{id: string, el: HTMLElement, top: number}>} detail.sections - 标题清单及其计算位置(按 top 升序)
* @param {Window|HTMLElement} detail.container - 滚动容器
*/
/**
* 为单页文档实现目录滚动高亮(Scroll Spy)。
* - 监听滚动并计算活动区块
* - 支持偏移(用于固定头部等布局)
* - 支持节流,降低滚动处理负载
* - 提供 onChange 回调,以便外部更新目录高亮和 ARIA 属性
*
* 无返回值。函数会为相应容器绑定滚动和尺寸变化监听。
*
* 可接收自定义事件:document.dispatchEvent(new Event('scrollspy:refresh')) 以手动刷新位置缓存。
*
* @param {string} containerSelector - 滚动容器选择器;使用 'window'(或传空/无效)则监听窗口滚动
* @param {string} headingsSelector - 标题选择器(建议限定到带 id 的标题,如 'main h2[id], main h3[id]')
* @param {number} [offset=0] - 计算活动区块时的像素偏移(正值等效于向下移动参考线)
* @param {number} [throttleMs=100] - 滚动与尺寸变更处理的节流间隔(毫秒,推荐 50-150)
* @param {ScrollSpyOnChange} [onChange] - 活动区块变化时触发的回调
* @returns {void}
*/
export function initScrollSpy(
containerSelector,
headingsSelector,
offset = 0,
throttleMs = 100,
onChange
) {
// SSR/非浏览器环境保护
if (typeof window === 'undefined' || typeof document === 'undefined') {
return;
}
// 参数校验
const isString = (v) => typeof v === 'string';
if (containerSelector != null && !isString(containerSelector)) {
throw new TypeError('initScrollSpy: containerSelector 必须为字符串。');
}
if (!isString(headingsSelector) || !headingsSelector.trim()) {
throw new TypeError('initScrollSpy: headingsSelector 必须为非空字符串。');
}
if (typeof offset !== 'number' || !Number.isFinite(offset)) {
throw new TypeError('initScrollSpy: offset 必须为有限数字。');
}
if (typeof throttleMs !== 'number' || !Number.isFinite(throttleMs) || throttleMs < 0) {
throw new TypeError('initScrollSpy: throttleMs 必须为大于等于 0 的有限数字。');
}
if (onChange != null && typeof onChange !== 'function') {
throw new TypeError('initScrollSpy: onChange 必须为函数(或省略)。');
}
const safeThrottleMs = Math.max(16, Math.floor(throttleMs) || 0); // 保底 ~1 帧
// 选择滚动容器
let container;
const wantWindow =
!containerSelector ||
containerSelector === 'window' ||
containerSelector === 'document' ||
!document.querySelector(containerSelector);
if (wantWindow) {
container = window;
} else {
container = document.querySelector(containerSelector);
}
// 在容器范围内查询标题(window 使用 document 范围)
const scope = container === window ? document : container;
let headings = Array.from(scope.querySelectorAll(headingsSelector)).filter(
(el) => el instanceof HTMLElement
);
// 过滤无 id 的标题,并提示
const noIdCount = headings.filter((h) => !h.id).length;
if (noIdCount > 0) {
// 仅提示一次,避免刷屏
console.warn(
`initScrollSpy: 检测到 ${noIdCount} 个未设置 id 的标题,这些标题将不会参与滚动高亮。`
);
headings = headings.filter((h) => !!h.id);
}
if (headings.length === 0) {
console.warn('initScrollSpy: 未找到任何可用标题,函数将不执行。');
return;
}
// 工具函数
const safeCssEscape = (s) => {
if (typeof CSS !== 'undefined' && typeof CSS.escape === 'function') {
return CSS.escape(s);
}
// 简易转义:为特殊字符加反斜杠
return String(s).replace(/([ !"#$%&'()*+,./:;<=>?@[\\\]^`{|}~])/g, '\\$1');
};
const getScrollTop = () => {
if (container === window) {
return (
window.pageYOffset ||
document.documentElement.scrollTop ||
document.body.scrollTop ||
0
);
}
return container.scrollTop;
};
const getTopWithinContainer = (el) => {
if (container === window) {
const rect = el.getBoundingClientRect();
const pageY =
window.pageYOffset ||
document.documentElement.scrollTop ||
document.body.scrollTop ||
0;
return rect.top + pageY;
}
const elRect = el.getBoundingClientRect();
const cRect = container.getBoundingClientRect();
return elRect.top - cRect.top + container.scrollTop;
};
const throttle = (fn, wait) => {
let last = 0;
let timer = null;
let lastArgs;
return function throttled(...args) {
const now = Date.now();
const remain = wait - (now - last);
lastArgs = args;
if (remain <= 0) {
if (timer) {
clearTimeout(timer);
timer = null;
}
last = now;
fn.apply(this, args);
} else if (!timer) {
timer = setTimeout(() => {
last = Date.now();
timer = null;
fn.apply(this, lastArgs);
}, remain);
}
};
};
// 数据缓存
let sections = [];
let lastActiveId = null;
const refreshSections = () => {
sections = headings
.map((el) => ({
id: el.id,
el,
top: Math.floor(getTopWithinContainer(el)),
}))
.sort((a, b) => a.top - b.top);
};
const pickActive = () => {
if (!sections.length) return { active: null, index: -1 };
const y = getScrollTop() + offset + 1; // +1 避免边界抖动
let idx = -1;
for (let i = 0; i < sections.length; i += 1) {
if (sections[i].top <= y) idx = i;
else break;
}
if (idx === -1) idx = 0; // 顶部区域默认选中第一个标题
return { active: sections[idx], index: idx };
};
const defaultOnChange = ({ activeId, previousId }) => {
// 默认行为:为匹配 a[href="#id"] 的目录链接设置 aria-current
// 你可以在自定义 onChange 中执行更复杂的 class 切换与滚动联动
if (!activeId && !previousId) return;
const qa = (sel) => Array.from(document.querySelectorAll(sel));
if (previousId) {
const prevSel = `a[href="#${safeCssEscape(previousId)}"]`;
qa(prevSel).forEach((a) => a.removeAttribute('aria-current'));
}
if (activeId) {
const nextSel = `a[href="#${safeCssEscape(activeId)}"]`;
qa(nextSel).forEach((a) => a.setAttribute('aria-current', 'true'));
}
};
const emitChange = ({ active, index }) => {
const activeId = active ? active.id : null;
const previousId = lastActiveId;
if (activeId === lastActiveId) return;
lastActiveId = activeId;
const detail = {
activeId,
previousId,
activeElement: active ? active.el : null,
index: active ? index : -1,
sections,
container,
};
try {
if (typeof onChange === 'function') {
onChange(detail);
} else {
defaultOnChange(detail);
}
} catch (err) {
// 防御性:避免回调抛错影响滚动
console.error('initScrollSpy: onChange 执行出错:', err);
}
};
const update = () => {
if (!sections.length) return;
const { active, index } = pickActive();
emitChange({ active, index });
};
// 组合:刷新 + 更新
const refreshAndUpdate = () => {
refreshSections();
update();
};
// 事件绑定(被动监听以提升滚动流畅度)
const onScroll = throttle(update, safeThrottleMs);
const onResize = throttle(refreshAndUpdate, Math.max(safeThrottleMs, 100));
const addListeners = () => {
const opts = { passive: true };
if (container === window) {
window.addEventListener('scroll', onScroll, opts);
} else {
container.addEventListener('scroll', onScroll, opts);
}
window.addEventListener('resize', onResize, opts);
window.addEventListener('orientationchange', onResize, opts);
window.addEventListener('load', refreshAndUpdate, opts);
// 提供手动刷新钩子(适合动态内容变更后)
document.addEventListener('scrollspy:refresh', refreshAndUpdate, opts);
};
// 初始化
refreshAndUpdate();
addListeners();
// 注意:根据需求可返回 teardown 以解除监听,但本实现遵循返回 void 的约束。
}
// 固定头部高度 64px,监听主内容区的二、三级标题
initScrollSpy(
'main',
'h2[id], h3[id]',
64, // offset: 顶部固定导航高度
100, // throttleMs: 节流间隔
({ activeId, previousId, activeElement }) => {
// 自定义:更新目录高亮与可访问性
const toc = document.querySelector('.toc');
if (!toc) return;
// 清除旧状态
toc.querySelectorAll('[aria-current="true"]').forEach((a) => {
a.removeAttribute('aria-current');
a.classList.remove('is-active');
});
// 设置新状态
if (activeId) {
// 注意使用 CSS.escape 以避免特殊字符选择器问题
const esc = (s) =>
(window.CSS && CSS.escape) ? CSS.escape(s) : s.replace(/([ !"#$%&'()*+,./:;<=>?@[\\\]^`{|}~])/g, '\\$1');
const links = toc.querySelectorAll(`a[href="#${esc(activeId)}"]`);
links.forEach((a) => {
a.setAttribute('aria-current', 'true');
a.classList.add('is-active');
});
}
// 可选(辅助键盘焦点可达性):确保标题可聚焦
if (activeElement && activeElement.tabIndex < 0) {
activeElement.tabIndex = -1; // 仅通过脚本聚焦时可用,不影响顺序
}
}
);
// 动态内容插入图片/异步渲染后(标题位置发生改变)手动刷新:
document.dispatchEvent(new Event('scrollspy:refresh'));
面向前端工程师、全栈开发者与技术团队,帮助在最短时间内产出结构清晰、风格统一、可直接合并的 JavaScript 函数声明。通过一次配置函数名称、意图、参数与返回值,自动生成含完整注释、错误处理建议与示例用法的高质量代码,显著缩短开发与评审周期,降低遗漏与返工;适用于通用工具、数据处理、页面交互与网络封装等高频场景,助力个人高效交付、团队标准落地与知识沉淀,并以可复用的提示词方案拉动转化与复购。
新建模块时一键生成规范函数与注释,快速落地交互逻辑;重构旧代码为清晰结构,缩短评审与联调时间。
为数据处理、工具库与浏览器逻辑快速产出稳健函数,统一命名与风格,减少后端对接与前端适配摩擦。
用统一模板落地团队编码约定,规范错误处理与注释标准;帮助新人快速上手,降低线上缺陷率。
将模板生成的提示词复制粘贴到您常用的 Chat 应用(如 ChatGPT、Claude 等),即可直接对话使用,无需额外开发。适合个人快速体验和轻量使用场景。
把提示词模板转化为 API,您的程序可任意修改模板参数,通过接口直接调用,轻松实现自动化与批量处理。适合开发者集成与业务系统嵌入。
在 MCP client 中配置对应的 server 地址,让您的 AI 应用自动调用提示词模板。适合高级用户和团队协作,让提示词在不同 AI 工具间无缝衔接。
半价获取高级提示词-优惠即将到期