mazey
v5.10.4
Published
A functional library for daily frontend work.
Downloads
3,021
Maintainers
Readme
English | 简体中文
Mazey
Mazey 是一个面向日常前端开发的函数工具库。前端生态已有许多优秀的库,但项目通常仍会创建 utils.js 或 common.js,用于存放通用函数。在多个项目之间重复复制相似函数既烦琐,也难以维护。因此,我创建了 Mazey,并会持续更新,为前端开发提供可靠的通用工具。
安装
通过 npm 安装 Mazey。
npm install mazey通过内容分发网络 (Content Delivery Network,CDN) 使用 Mazey。
<script src="https://cdn.jsdelivr.net/npm/mazey@latest/lib/mazey.min.js"></script>你也可以下载 jsdelivr/lib/mazey.min.js,并自行托管该文件。
浏览器支持
Mazey 支持 Chrome 109 及以上版本、Edge 109 及以上版本、Firefox 115 及以上版本、Safari 16.4 及以上版本、iOS Safari 16.4 及以上版本、Android Chrome 109 及以上版本,以及 Samsung Internet 21 及以上版本。软件包输出可能包含 ES2022 语法,并且不包含 JavaScript polyfill。Internet Explorer、Opera Mini、KaiOS、旧版 Android Browser 和更早的浏览器版本不在支持范围内。
开发和持续集成环境使用 Node.js 22。Mazey 未声明 Node.js 运行时兼容范围。
使用
下面的示例使用一个函数,判断某个值是否适合参与常规计算和比较。
通过 npm 导入。
import { isNumber } from "mazey";
const x = 123;
const y = "abc";
const z = Infinity;
isNumber(x); // 输出: true
isNumber(y); // 输出: false
isNumber(z, { isInfinityAsNumber: true }); // 输出: true通过 CDN 导入。
<script src="https://cdn.jsdelivr.net/npm/mazey@latest/lib/mazey.min.js"></script>
<script>
const x = 123;
mazey.isNumber(x); // 输出: true
</script>API 示例
下面列出一些手动维护的 API (应用程序编程接口) 示例。完整内容请查看 完整 API 文档。
目录
加载资源
loadScript
从服务器加载并执行 JavaScript 文件。
用法:
loadScript(
"http://example.com/static/js/plugin-2.1.1.min.js",
{
id: "iamid", // 可选,script 的 ID,默认不设置
timeout: 5000, // 可选,超时时间,默认 `5000`
}
)
.then(
res => {
console.log(`加载 JavaScript 脚本: ${res}`);
}
)
.catch(
err => {
console.error(`加载 JavaScript 脚本: ${err.message}`);
}
);输出:
加载 JavaScript 脚本: loadedloadScriptIfUndefined
当脚本尚未加载时,函数会通过指定 URL (统一资源定位符) 加载脚本。该函数使用 window["attribute"] 判断脚本是否已定义。
用法:
loadScriptIfUndefined("xyz", "https://example.com/lib/xyz.min.js")
.then(() => {
console.log("xyz 已加载。");
})
.catch(err => {
console.log("xyz 加载失败。", err);
});输出:
xyz 已加载。loadCSS
从服务器加载 CSS 文件。
用法:
loadCSS(
"https://example.com/path/example.css",
{
id: "iamid", // 可选,link 的 ID,默认不设置
}
)
.then(
res => {
console.log(`CSS 加载成功: ${res}`);
}
)
.catch(
err => {
console.error(`CSS 加载失败: ${err.message}`)
}
);输出:
CSS 加载成功: loadedloadImage
从指定 URL 加载图片。
目标图片会在后台加载。加载失败时,Promise (承诺对象) 会以错误对象进入 reject 状态。加载成功时,Promise 会以图片对象进入 resolve 状态。该方法适合预加载图片,也可以利用浏览器缓存实现图片懒加载。
该方法不会把图片添加到文档对象模型 (Document Object Model,DOM)。
用法:
loadImage("https://example.com/example.png")
.then((img) => {
console.log(img);
})
.catch((err) => {
console.log(err);
});windowLoaded
检查页面是否成功加载。即使浏览器已经触发 load 事件,该函数仍能正确处理。
用法:
windowLoaded()
.then(res => {
console.log(`加载成功: ${res}`);
})
.catch(err => {
console.log(`加载超时或失败: ${err.message}`);
});输出:
加载成功: load通用工具
使用原生 Date.now() 获取当前时间戳 (毫秒)。原有的 mNow() 工具仍作为弃用兼容 API
保留。
isNumber
判断某个值是否为有效数字。
用法:
const ret1 = isNumber(123);
const ret2 = isNumber("123");
// 默认情况下,NaN 和 Infinity 不是有效数字
const ret3 = isNumber(Infinity);
const ret4 = isNumber(Infinity, { isInfinityAsNumber: true });
const ret5 = isNumber(NaN);
const ret6 = isNumber(NaN, { isNaNAsNumber: true, isInfinityAsNumber: true });
console.log(ret1, ret2, ret3, ret4, ret5, ret6);输出:
true false false true false trueisNullish
判断值是否严格为 undefined 或 null。其他假值不属于空值。
isNullish(undefined); // true
isNullish(null); // true
isNullish(false); // false
isNullish(0); // false
isNullish(""); // falseisUdfOrNul 仍作为 isNullish 的弃用别名保留。
isJSONString
判断字符串是否为有效的 JSON 字符串。
用法:
const ret1 = isJSONString(`['a', 'b', 'c']`);
const ret2 = isJSONString(`["a", "b", "c"]`);
console.log(ret1);
console.log(ret2);输出:
false
trueisValidData
判断指定路径中的数据是否有效,并且是否等于预期值。
用法:
const validData = {
a: {
b: {
c: 413
}
}
};
const isValidDataResA = isValidData(validData, ["a", "b", "c"], 2333);
const isValidDataResB = isValidData(validData, ["a", "b", "c"], 413);
const isValidDataResC = isValidData(validData, ["d", "d"], 413);
console.log("isValidDataResA:", isValidDataResA);
console.log("isValidDataResB:", isValidDataResB);
console.log("isValidDataResC:", isValidDataResC);输出:
isValidDataResA: false
isValidDataResB: true
isValidDataResC: falsegenRndNumString
生成指定长度的随机数字字符串。例如,genRndNumString(7) 可能返回 "7658495"。
用法:
const ret1 = genRndNumString(4);
const ret2 = genRndNumString(7);
console.log(ret1);
console.log(ret2);输出:
9730
2262490formatDate
按照指定格式返回本地日期字符串。HH 表示 24 小时制,hh 表示 12 小时制,a 表示 AM 或 PM。
用法:
const ret1 = formatDate();
const ret2 = formatDate("Tue Jan 11 2022 14:12:26 GMT+0800 (China Standard Time)", "yyyy-MM-dd hh:mm:ss a");
const ret3 = formatDate(1641881235000, "yyyy-MM-dd hh:mm:ss a");
const ret4 = formatDate(new Date(2014, 1, 11), "MM/dd/yyyy");
console.log("默认 formatDate 值:", ret1);
console.log("字符串 formatDate 值:", ret2);
console.log("数字 formatDate 值:", ret3);
console.log("Date formatDate 值:", ret4);输出:
默认 formatDate 值: 2023-01-11
字符串 formatDate 值: 2022-01-11 02:12:26 PM
数字 formatDate 值: 2022-01-11 02:07:15 PM
Date formatDate 值: 02/11/2014isValidDate
检查未知值是否表示有效日期。函数接受 Date 实例、有限的毫秒时间戳、受支持的本地日期字符串,以及带有 Z 或数字时区偏移的 ISO 8601 字符串。
字符串格式包括 YYYY-MM-DD、YYYY-MM-DD HH:mm[:ss] 和 YYYY-MM-DDTHH:mm[:ss]。使用 T 分隔的日期时间还可以包含 Z 或 +HH:mm、-HH:mm 时区偏移。带时区的字符串可以包含 1~3 位毫秒数字。
函数会把结构化字符串解析为数字组件,并执行严格校验。因此,"2020-02-30" 等无效日历日期不会被转换为其他日期。
用法:
const ret1 = isValidDate(1577877720000);
const ret2 = isValidDate("2020-01-01 11:22");
const ret3 = isValidDate("2020-02-30");
const ret4 = isValidDate(new Date("invalid"));
console.log(ret1, ret2, ret3, ret4);输出:
true true false falsegenerateCalendarVersion
按照本地时间生成日历版本字符串。其概念格式为 yyyy.MMdd.HHmmss。函数会删除每个数字段的前导零。处理后的结果兼容语义化版本 (SemVer)。
在系统时钟正常前进时,版本会随日期和时间递增。该函数有意使用本地时间。因此,手动回拨时钟或夏令时回退可能生成更小的版本。
用法:
const version = generateCalendarVersion(
new Date(2026, 6, 11, 7, 40, 35)
);
console.log(version);输出:
2026.711.74035getDateDifference
计算两个日期或时间戳之间的差值。默认返回完整秒数。type: "d" 返回完整天数,type: "text" 返回由天、小时、分钟和秒组成的英文文本。文本会省略值为 0 的单位。总时长为零时,函数返回 "0 seconds"。
YYYY-MM-DD HH:mm:ss 格式的字符串按本地时间解析。其他日期字符串使用运行环境原生的 Date 解析器。需要跨环境稳定解析时,请使用时间戳,或使用包含明确时区的 ISO 8601 字符串。结束时间早于开始时间,或任一日期无效时,函数返回空字符串。
用法:
const days = getDateDifference(0, 90061000, { type: "d" });
const text = getDateDifference(0, 90061000, { type: "text" });
const compactText = getDateDifference(0, 90060000, { type: "text" });
const dateStringDays = getDateDifference(
"2020-03-28 00:09:27",
"2023-04-18 10:54:00",
{ type: "d" }
);
console.log(days);
console.log(text);
console.log(compactText);
console.log(dateStringDays);输出:
1
1 day 1 hour 1 minute 1 second
1 day 1 hour 1 minute
1116getFriendlyInterval 是 getDateDifference 的兼容别名。新代码建议使用 getDateDifference。
formatDurationFromMs
将毫秒时长转换为适用的最大单位,包括秒、分钟、小时和天。结果最多保留 1 位小数。负数和非有限值返回 "0 seconds"。
用法:
const ret1 = formatDurationFromMs(500);
const ret2 = formatDurationFromMs(90000);
const ret3 = formatDurationFromMs(3600000);
const ret4 = formatDurationFromMs(129600000);
console.log(ret1);
console.log(ret2);
console.log(ret3);
console.log(ret4);输出:
0.5 seconds
1.5 minutes
1 hour
1.5 daysdeepCopy
深度复制或克隆对象。
用法:
const ret1 = deepCopy(["a", "b", "c"]);
const ret2 = deepCopy("abc");
console.log(ret1);
console.log(ret2);输出:
["a", "b", "c"]
abcdeepFreeze
递归冻结对象及其可枚举嵌套值。原始值和已经冻结的对象会原样返回。函数支持包含循环引用的对象。
用法:
const config = deepFreeze({
api: {
timeout: 5000,
},
});
console.log(Object.isFrozen(config));
console.log(Object.isFrozen(config.api));输出:
true
truedebounce
创建防抖函数。
用法:
const foo = debounce(() => {
console.log("防抖函数会在 1,000 毫秒内仅执行一次,等待期间的其他调用不会生效。");
}, 1000, true);repeatUntilConditionMet
按顺序轮询,直到结果严格等于 true、自定义条件满足,或达到调用次数上限。
默认间隔为 1000 毫秒,调用次数上限为 10 次。首次调用等待一个间隔;
后续间隔从前一次回调完成后开始计算。
const cancelPolling = repeatUntilConditionMet(
fetchStatus,
{ interval: 1000, times: 10 },
result => result === true
);
// During component unmount or owner teardown:
cancelPolling();返回的清理函数可重复调用。它会清除待执行的定时器,并在正在执行的回调完成后, 阻止条件判断和后续轮询。它不会中止当前回调、取消网络请求或撤销副作用。 现有验证失败和调用次数为零的路径也返回安全的清理函数,不安排定时器。 回调和条件函数的异常保持原有行为,不会被抑制。
throttle
创建节流函数。
用法:
const foo = throttle(() => {
console.log("在每个 1,000 毫秒等待周期内,该函数最多执行一次。");
}, 1000, { leading: true });参考资料: Lodash
convertCamelToKebab
将驼峰命名转换为短横线命名。
用法:
const ret1 = convertCamelToKebab("ABC");
const ret2 = convertCamelToKebab("aBC");
console.log(ret1);
console.log(ret2);输出:
a-b-c
a-b-cconvertCamelToSnake
在每个 ASCII 大写字母前插入下划线,将结果转换为小写,并删除第一个前导下划线。
XMLParser 转换为 x_m_l_parser,其余已有下划线保留。
convertCamelToUnder 保留为弃用的直接别名,camelCase2Underscore 仍可使用。
用法:
const ret1 = convertCamelToSnake("ABC");
const ret2 = convertCamelToSnake("aBC");
console.log(ret1);
console.log(ret2);输出:
a_b_c
a_b_cconvertSnakeToCamel
将下划线及其后的 ASCII 小写字母替换为大写字母,其他字符保持不变。
convertUnderToCamel 保留为弃用的直接别名。
convertSnakeToCamel("a_b_c"); // "aBC"
convertSnakeToCamel("a__b_"); // "a_B_"formatPercentage
将数值比率乘以 100 并添加 %。小数位数默认为 0。
小数位数为假值时使用 Math.floor,负数也向下取整;为真值时使用原生 toFixed,
保留末尾零、浮点数行为,以及不支持的小数位数导致的异常。
floatToPercent 保留为弃用的直接别名。
formatPercentage(0.129); // "12%"
formatPercentage(-0.129); // "-13%"
formatPercentage(0.125, 1); // "12.5%"
formatPercentage(0.12, 2); // "12.00%"toJavaScriptGlobalName
将文本转换为确定的、全大写的 ASCII JavaScript 标识符。该标识符适合作为 IIFE 全局名称。函数会将无效字符替换为下划线。结果以数字开头时,函数会添加下划线前缀。
const globalName = toJavaScriptGlobalName("@scope/my-library");
console.log(globalName);输出:
_SCOPE_MY_LIBRARYURL
getQueryParam
从当前 Web URL 的查询字符串 (location.search) 中获取参数值。
用法:
// http://example.com/?t1=1&t2=2&t3=3&t4=4#2333
// ?t1=1&t2=2&t3=3&t4=4
const p1 = getQueryParam("t3");
const p2 = getQueryParam("t4");
console.log(p1, p2);输出:
3 4getUrlParam
从输入 URL 中获取指定查询参数的值。
用法:
const p1 = getUrlParam("https://example.com/?t1=1&t2=2&t3=3&t4=4", "t3");
const p2 = getUrlParam("https://example.com/?t1=1&t2=2&t3=3&t4=4", "t4");
console.log(p1, p2);输出:
3 4getHashQueryParam
从当前 Web URL 的哈希字符串 (location.hash) 中获取参数值。
用法:
// http://example.com/?#2333?t1=1&t2=2&t3=3&t4=4
// #2333?t1=1&t2=2&t3=3&t4=4
const p1 = getHashQueryParam("t3");
const p2 = getHashQueryParam("t4");
console.log(p1, p2);输出:
3 4getDomain
获取 URL 的域名,也可以组合返回其他部分。
用法:
const ret1 = getDomain("http://example.com/?t1=1&t2=2&t3=3&t4=4");
const ret2 = getDomain("http://example.com/test/thanks?t1=1&t2=2&t3=3&t4=4", ["hostname", "pathname"]);
const ret3 = getDomain("http://example.com:7890/test/thanks", ["hostname"]);
const ret4 = getDomain("http://example.com:7890/test/thanks", ["host"]); // 包含端口
const ret5 = getDomain("http://example.com:7890/test/thanks", ["origin"]);
const ret6 = getDomain("http://example.com:7890/test/thanks?id=1", ["origin", "pathname", "search"]);
console.log(ret1);
console.log(ret2);
console.log(ret3);
console.log(ret4);
console.log(ret5);
console.log(ret6);输出:
example.com
example.com/test/thanks
example.com
example.com:7890
http://example.com:7890
http://example.com:7890/test/thanks?id=1updateQueryParam
更新输入 URL 中的查询参数值。
用法:
const ret1 = updateQueryParam("http://example.com/?t1=1&t2=2&t3=3&t4=4", "t3", "three");
const ret2 = updateQueryParam("http://example.com/?t1=1&t2=2&t3=3&t4=4", "t4", "four");
console.log(ret1);
console.log(ret2);输出:
http://example.com/?t1=1&t2=2&t3=three&t4=4
http://example.com/?t1=1&t2=2&t3=3&t4=fourisValidUrl
检查给定字符串是否为有效 URL,包括使用其他协议的 URL。
用法:
const ret1 = isValidUrl("https://www.example.com");
const ret2 = isValidUrl("http://example.com/path/exx/ss");
const ret3 = isValidUrl("https://www.example.com/?q=hello&age=24#world");
const ret4 = isValidUrl("http://www.example.com/#world?id=9");
const ret5 = isValidUrl("ftp://example.com");
console.log(ret1, ret2, ret3, ret4, ret5);输出:
true true true true true如果只需检查 HTTP 或 HTTPS URL,建议使用 isValidHttpUrl。isValidUrl 会匹配所有协议 URL,包括 FTP 和其他非 HTTP 协议。
isValidHttpUrl
检查给定字符串是否为有效的 HTTP 或 HTTPS URL。
用法:
const ret1 = isValidHttpUrl("https://www.example.com");
const ret2 = isValidHttpUrl("http://example.com/path/exx/ss");
const ret3 = isValidHttpUrl("https://www.example.com/?q=hello&age=24#world");
const ret4 = isValidHttpUrl("http://www.example.com/#world?id=9");
const ret5 = isValidHttpUrl("ftp://example.com");
console.log(ret1, ret2, ret3, ret4, ret5);输出:
true true true true falseparseGitHubRepository
解析 GitHub 仓库简写、SCP 格式或支持的 Git 传输 URL,并返回规范的仓库标识信息。
const repository = parseGitHubRepository("[email protected]:acme/widget.git");
console.log(JSON.stringify(repository));输出:
{"owner":"acme","name":"widget","slug":"acme/widget","url":"https://github.com/acme/widget"}存储
Cookie 工具
操作 Cookie。
用法:
setCookie("test", "123", 30, "example.com"); // 键、值、有效天数、域名
const ret = getCookie("test");
console.log(ret);输出:
123Storage 工具
在 Web Storage 中存储 JSON 序列化值,并在读取时解析这些值。
用法:
setSessionJSON("preferences", { theme: "dark" });
const sessionValue = getSessionJSON("preferences");
setLocalJSON("recentItems", [ "one", "two" ]);
const localValue = getLocalJSON("recentItems");
console.log({ sessionValue, localValue });
// 也可以按项目封装键名。
const projectName = "mazey";
function mSetLocalStorage (key, value) {
return setLocalJSON(`${projectName}_${key}`, value);
}
function mGetLocalStorage (key) {
return getLocalJSON(`${projectName}_${key}`);
}输出:
{
sessionValue: { theme: "dark" },
localValue: [ "one", "two" ]
}setSessionStorage、getSessionStorage、setLocalStorage 和
getLocalStorage 是对应 JSON 工具的弃用别名。
DOM
Class 工具
操作元素的 class。
用法:
const dom = document.querySelector("#box");
// 判断 class
hasClass(dom, "test");
// 添加 class
addClass(dom, "test");
// 删除 class
removeClass(dom, "test");hideElements 和 showElements
隐藏或显示 CSS 选择器、单个元素、可迭代元素集合或类数组元素集合。两个函数都会返回原始输入。 重复元素只会被修改一次。函数会忽略无效选择器和不支持的值。
hideElements() 会保存可见元素的内联 display 值。showElements() 会恢复该值。如果样式表仍隐藏该元素,函数会恢复元素的默认显示方式。
import { hideElements, showElements } from "mazey";
const notices = document.querySelectorAll(".notice");
hideElements(notices);
showElements(notices);
hideElements("#temporary-message");
showElements(document.querySelector("#temporary-message"));hide 和 show 仍作为弃用别名保留。
injectStyle
在 <head> 中添加 <style> 元素。
addStyle 是 injectStyle 的弃用兼容别名。
用法:
示例 1: 添加带有 id 的 <style>。重复调用会更新内容,不会添加新元素。
import { injectStyle } from "mazey";
injectStyle(
"body { background-color: #333; }",
{ id: "test" }
);输出:
<style id="test">body { background-color: #333; }</style>示例 2: 添加不带 id 的 <style>。重复调用会添加新元素。
import { injectStyle } from "mazey";
injectStyle("body { background-color: #444; }");输出:
<style>body { background-color: #444; }</style>示例 3: 组合使用 genStyleString 和 injectStyle,一次添加多条样式。
import { genStyleString, injectStyle } from "mazey";
const xStyle = genStyleString(
".footer>.x-wish>a:first-child" +
",div.wish-flex>a[href^='https://github.com/chengchuu']" +
",.m-hide",
[ "display: none" ]
);
const yStyle = genStyleString(
".footer>.y-wish:before",
[
`content: 'Copyright (c) chengchuu'`,
"color: inherit",
"padding-inline-start: var(--y-wish-1_5)",
"padding-inline-end: var(--y-wish-1_5)",
"padding-top: var(--y-wish-1)",
"padding-bottom: var(--y-wish-1)",
]
);
injectStyle(xStyle + yStyle, { id: "z-style" });输出:
<style id="z-style">.footer>.x-wish>a:first-child,div.wish-flex>a[href^='https://github.com/chengchuu'],.m-hide{display: none;}.footer>.y-wish:before{content: 'Copyright (c) chengchuu';color: inherit;padding-inline-start: var(--y-wish-1_5);padding-inline-end: var(--y-wish-1_5);padding-top: var(--y-wish-1);padding-bottom: var(--y-wish-1);}</style>genStyleString
根据参数生成样式字符串。第一个参数是查询选择器,第二个参数是样式数组。
用法:
const ret1 = genStyleString(".a", [ "color:red" ]);
const ret2 = genStyleString("#b", [ "color:red", "font-size:12px" ]);
console.log(ret1);
console.log(ret2);输出:
.a{color:red;}
#b{color:red;font-size:12px;}下面的示例组合使用 genStyleString 和 injectStyle,一次添加多条样式。
import { genStyleString, injectStyle } from "mazey";
const xStyle = genStyleString(
".footer>.x-wish>a:first-child" +
",div.wish-flex>a[href^='https://github.com/chengchuu']" +
",.m-hide",
[ "display: none" ]
);
const yStyle = genStyleString(
".footer>.y-wish:before",
[
`content: 'Copyright (c) chengchuu'`,
"color: inherit",
"padding-inline-start: var(--y-wish-1_5)",
"padding-inline-end: var(--y-wish-1_5)",
"padding-top: var(--y-wish-1)",
"padding-bottom: var(--y-wish-1)",
]
);
injectStyle(xStyle + yStyle, { id: "z-style" });输出:
<style id="z-style">.footer>.x-wish>a:first-child,div.wish-flex>a[href^='https://github.com/chengchuu'],.m-hide{display: none;}.footer>.y-wish:before{content: 'Copyright (c) chengchuu';color: inherit;padding-inline-start: var(--y-wish-1_5);padding-inline-end: var(--y-wish-1_5);padding-top: var(--y-wish-1);padding-bottom: var(--y-wish-1);}</style>newLine
把文本中的换行符转换为 HTML 换行元素。
用法:
const ret1 = newLine("a\nb\nc");
const ret2 = newLine("a\n\nbc");
console.log(ret1);
console.log(ret2);输出:
a<br />b<br />c
a<br /><br />bc事件
onEvent
注册具名的 Mazey 事件回调。函数允许重复注册同一个回调。
addEvent 是 onEvent 的弃用兼容别名。
import { fireEvent, onEvent } from "mazey";
onEvent("test", event => {
console.log("test event:", event);
});
fireEvent("test", { type: "test" });计算与公式
calculateAspectRatio
根据正安全整数形式的宽度和高度,计算精确的最简宽高比。函数使用最大公约数约分,并使用小写 x 连接结果。函数不会将结果近似为常见的图片或视频宽高比。
import { calculateAspectRatio } from "mazey";
const portraitRatio = calculateAspectRatio(900, 1200);
const landscapeRatio = calculateAspectRatio(1920, 1080);
console.log(portraitRatio);
console.log(landscapeRatio);输出:
3x4
16x9例如,calculateAspectRatio(3440, 1440) 返回数学意义上精确的 "43x18",而不是近似标签 "21x9"。无效或不安全的整数尺寸会抛出 TypeError。零或负数尺寸会抛出 RangeError。
calculateCAGR
根据投资的开始日期、结束日期和整个周期的总回报率,计算复合年增长率(Compound Annual Growth Rate,CAGR)。
CAGR = (1 + totalReturnRate)^(365 / durationInDays) - 1日期可以是支持的结构化日期字符串、毫秒时间戳或 Date 实例。计算使用精确的毫秒间隔,包括日期中的时分秒,并固定以 365 天作为一个财务年度。
数值输入使用十进制比率,因此 0.202 表示 20.2%。字符串输入使用百分比数值,因此 "20.2%" 和 "20.2" 都表示 20.2%;也支持 "2.02e1%" 这类严格的科学记数法。返回的 CAGR 是未经舍入的十进制比率。
import { calculateCAGR, formatPercentage } from "mazey";
const cagr = calculateCAGR(
"2022-04-01",
"2025-10-01",
"20.2%"
);
console.log({
cagr,
percentage: formatPercentage(cagr, 2),
});可能的输出:
{
cagr: 0.053908...,
percentage: "5.39%"
}等效的十进制数值调用如下:
calculateCAGR(
"2022-04-01",
"2025-10-01",
0.202
);日期字符串遵循 Mazey 的严格日期校验规则。无效日期、格式错误或非有限的回报率,以及没有递增的日期范围都会抛出错误。解析后的总回报率必须大于 -1,因为 -1 表示本金完全损失,此时 CAGR 没有定义。
randomBoolean
判断生成的随机值是否小于指定概率。
用法:
const ret = randomBoolean(0.5); // 有 50% 的概率返回 true
console.log(ret);输出:
true下面的示例测试概率精度。
// 测试
let trueCount = 0;
let falseCount = 0;
new Array(1000000).fill(0).forEach(() => {
if (randomBoolean(0.5)) {
trueCount++;
} else {
falseCount++;
}
});
console.log(trueCount, falseCount); // 499994 500006randomBoolean 直接计算 Math.random() < rate,不会限制传入的概率值。isHit 是弃用别名。
inRate 仍作为兼容别名保留。
longestComSubstring
计算两个字符串的最长公共子串长度。
用法:
const ret = longestComSubstring("fish", "finish");
console.log(ret);输出:
3longestComSubsequence
计算两个字符串的最长公共子序列长度。
用法:
const ret = longestComSubsequence("fish", "finish");
console.log(ret);输出:
4浏览器信息
detectVisitorType
此函数使用保守的启发式规则,将访问者分类为 "crawler"、"automation" 或 "unknown"。函数首先检查一组明确的 User-Agent 令牌。这些令牌来自爬虫、索引、SEO、AI 抓取和链接预览客户端。随后,函数检查显式的自动化 User-Agent 令牌,以及 navigator.webdriver === true。
省略参数时,函数会安全地读取 navigator.userAgent。也可以传入明确的 User-Agent 字符串。此方式适合分析已捕获的 User-Agent、编写确定性测试或在服务端分类。SSR 或 Node.js 环境没有 navigator 时,默认返回 "unknown"。此时仍可传入明确的 User-Agent 进行分类。
const visitorType = detectVisitorType();
console.log(visitorType);可能的输出:
unknown下面的示例传入爬虫 User-Agent:
const visitorType = detectVisitorType(
"Mozilla/5.0 (compatible; Googlebot/2.1)"
);
console.log(visitorType);输出:
crawler"unknown" 仅表示没有检测到受支持的爬虫或浏览器自动化信号。User-Agent 可以伪造,WebDriver 信号也可以隐藏或修改。因此,分类可能出现误判或漏判。
unknown不表示访问者已经通过真人验证。此函数只使用浏览器端启发式规则,不能作为安全边界。请勿单独使用此结果进行身份验证、授权、支付决策、速率限制、欺诈防范或访问控制。验证真实爬虫通常需要服务端请求信息,以及服务提供商规定的验证流程。
isPhone
检查当前浏览器是否代表手机或手持设备。此结果不包含平板电脑。
const result = isPhone();
console.log(result);isDesktop
检查当前浏览器是否代表桌面或笔记本电脑。触摸屏 Windows 笔记本电脑仍归类为桌面设备。
const result = isDesktop();
console.log(result);isTablet
检查当前浏览器是否代表平板电脑。此函数支持常规 iPad 和 iPadOS 桌面模式。他还支持不含 Mobile 令牌的 Android User-Agent,以及独立的 Tablet 令牌。
const result = isTablet();
console.log(result);可以传入 User-Agent 字符串。此方式适合确定性测试或服务端分类。
const result = isTablet(
"Mozilla/5.0 (Linux; Android 14; SM-X710) AppleWebKit/537.36"
);
console.log(result);输出:
true对于已识别的设备,这 3 个函数使用互斥的设备形态分类:
| 设备 | isPhone | isDesktop | isTablet |
|:-----------------|:-----------|:------------|:-----------|
| iPhone | true | false | false |
| Android 手机 | true | false | false |
| iPad | false | false | true |
| Android 平板电脑 | false | false | true |
| Windows 笔记本 | false | true | false |
| MacBook | false | true | false |
| 未知设备 | false | false | false |
每个函数都接受可选的 User-Agent 字符串。显式输入不会读取当前浏览器的平台或触摸信号。SSR 环境无法读取浏览器信号且没有显式输入时,这 3 个函数均返回 false。
设备分类使用可伪造的启发式规则,不读取视口宽度。这些函数不能作为安全 API,也不能替代响应式 CSS 和功能检测。getBrowserInfo().platform 保留原有的宽泛分类,并将 iOS 和 Android 报告为 "mobile"。新函数提供更具体的手机、平板电脑或桌面设备分类。
isPhone 用于检查设备形态。独立的 isMobile API 是 isValidPhoneNumber 的直接别名,用于验证 11 位中国手机号码形式的字符串,不会检查浏览器或设备。
getBrowserInfo
获取浏览器信息。
用法:
const ret = getBrowserInfo();
console.log(ret);输出:
{"engine":"webkit","engineVs":"537.36","platform":"desktop","supporter":"chrome","supporterVs":"85.0.4183.121","system":"windows","systemVs":"10"}返回字段:
| 字段 | 说明 | 类型 | 可选值 |
| :--- | :--- | :--- | :--- |
| system | 操作系统 | string | android、ios、windows、macos、linux |
| systemVs | 操作系统版本 | string | Windows: 2000、xp、2003、vista、7、8、8.1、10;macOS: ⋯⋯ |
| platform | 平台 | string | desktop、mobile |
| engine | 浏览器引擎 | string | webkit、gecko、presto、trident |
| engineVs | 浏览器引擎版本 | string | - |
| supporter | 浏览器 | string | edge、opera、chrome、safari、firefox、iexplore |
| supporterVs | 浏览器版本 | string | - |
| shell | 浏览器外壳 | string | 可选: wechat、qq_browser、qq_app、uc、360、2345、sougou、liebao、maxthon、bilibili |
| shellVs | 浏览器外壳版本 | string | 可选,例如 20 |
| appleType | Apple 设备类型 | string | 可选: ipad、iphone、ipod、iwatch |
下面的示例判断当前环境是否为移动版 QQ。
const { system, shell } = getBrowserInfo();
const isMobileQQ = ["android", "ios"].includes(system) && ["qq_browser", "qq_app"].includes(shell);isSafePWAEnv
检查当前浏览器文档是否满足 PWA (渐进式 Web 应用) 的最低前提条件。这里只检查同步 JavaScript 能够识别的条件。函数会检查安全上下文和 Service Worker API 支持。默认情况下,文档还必须包含 Web App Manifest (Web 应用清单) 链接,而且 href 不能为空。
只需检查安全的 Service Worker 环境时,可以传入 { requireManifest: false }。传入 { scope: "/app/" } 时,当前页面还必须位于同源路径范围内。
该检查不会验证或请求 Manifest。它也不会验证 Service Worker 是否注册成功。该函数无法判断应用是否已经安装,也不保证浏览器会提供安装提示。不同浏览器还可能执行额外的安装策略。
用法:
const ret = isSafePWAEnv();
console.log(ret);输出:
trueisStandalonePWA
检查当前页面是否以独立 PWA 模式显示。函数会检查标准的显示模式媒体查询,并兼容 iOS Safari 的 navigator.standalone。该结果只表示显示模式,不能证明应用已经安装或受 Service Worker 控制。
if (isStandalonePWA()) {
document.querySelector("[data-install-help]")?.remove();
}Web 性能
getPerformance
通过 PerformanceNavigationTiming 获取页面加载指标。
如果浏览器未提供导航条目,该函数返回的 Promise 会进入 rejected 状态。函数不会回退到已弃用的 PerformanceTiming API。
用法:
// `camelCase: false` (默认值) 返回下划线格式 (`a_b`) 的数据
// `camelCase: true` 返回驼峰格式 (`aB`) 的数据
getPerformance()
.then(res => {
console.log(JSON.stringify(res));
})
.catch(console.error);输出:
{"source":"PerformanceNavigationTiming","os":"others","os_version":"","device_type":"pc","network":"4g","screen_direction":"","unload_time":0,"redirect_time":0,"dns_time":0,"tcp_time":0,"ssl_time":0,"response_time":2,"download_time":2,"first_paint_time":288,"first_contentful_paint_time":288,"dom_ready_time":0,"onload_time":0,"white_time":0,"render_time":0,"decoded_body_size":718,"encoded_body_size":718}返回字段:
| 字段 | 说明 | 类型 | 计算方式 | | :--- | :--- | :--- | :--- | | dns_time | DNS 查询时间 | number | domainLookupEnd - domainLookupStart | | tcp_time | 连接协商时间 | number | connectEnd - connectStart | | response_time | 请求与响应时间 | number | responseStart - requestStart | | white_time | 白屏时间 | number | responseStart - navigationStart | | dom_ready_time | DOM 就绪时间 | number | domContentLoadedEventStart - navigationStart | | onload_time | 页面加载时间 | number | loadEventStart - navigationStart | | render_time | EventEnd 时间 | number | loadEventEnd - navigationStart | | unload_time | 页面卸载时间 | number | 可选: unloadEventEnd - unloadEventStart | | redirect_time | 重定向时间 | number | 可选: redirectEnd - redirectStart | | ssl_time | SSL 连接时间 | number | 可选: connectEnd - secureConnectionStart | | download_time | 下载时间 | number | 可选: responseEnd - responseStart |
调试
genCustomConsole
创建带有自定义前缀的控制台 (console) 输出对象。
用法:
const myConsole = genCustomConsole("MazeyLog:");
myConsole.log("I am string.");
myConsole.info("I am boolean.", true);
myConsole.info("I am number.", 123, 456);
myConsole.info("I am object.", { a: 123, b: 456});输出:
MazeyLog: I am string.
MazeyLog: I am boolean. true
MazeyLog: I am number. 123 456
MazeyLog: I am object. {a: 123, b: 456}参与贡献
开发环境
| 依赖 | 版本 | | --- | --- | | Node.js | v22.21.1 | | TypeScript | v5.1.6 |
脚本
安装依赖:
npm i启动开发环境:
npm run dev构建:
npm run build测试:
npm run test生成文档:
npm run docs返回值
| 值 | 说明 | 类型 | | :--- | :--- | :--- | | ok | 操作成功。 | string | | loaded | 资源已经加载。 | string | | failed | 发生错误。 | string | | defined | 值已定义。 | string | | undefined | 值未定义。 | string | | timeout | 操作超时。 | string | | true | 值为真。 | boolean | | false | 值为假。 | boolean |
许可证
本软件按照 MIT 许可证 发布。
