介绍
仓库地址: github.com/xiaotong-to…
仓库里面的函数都是我平时经常使用的的一些功能,可以用于日常开发,虽然目前包含的模块不多,但我会持续追加的,如果你对这个项目感兴趣,欢迎试用!(如果有想要的功能我这边也可以尽量满足,也欢迎提交 pr 和 issus)
使用方法
- 安装
npm i xtt-utils
然后在项目中导入就行,支持按需导入
import { random } from "xtt-utils";
random();
项目中的所有方法在项目下 docs 目录下都有文档说明,具体说明可以查看文档,或者访问 github page
Methods
trimLineStart
格式化行前的缩进层级
实现这个方法主要是为了格式化 HTML 代码的层级,因为使用了 Prettier,然后获取元素的 innerHTML 时,自然也把前面的缩进也带上了,如果不进一步处理的话这些文字的所有行始终都有个缩进
trimLineStart(" 123"); // -> 123
trimLineStart(`
<xtt-code>
<xtt-button>default</xtt-button>
<style>
.long-text {
max-width: 200px;
}
</style>
<xtt-button disabled>disabled</xtt-button>
</xtt-code>
`)
/** ->
<xtt-code>
<xtt-button>default</xtt-button>
<style>
.long-text {
max-width: 200px;
}
</style>
<xtt-button disabled>disabled</xtt-button>
</xtt-code>
这里格式化可能不是那么准确
*/
"--------------------------"
主要思路是先查找所有行的行前缩进的数目,然后找到最小的共同缩进数,再把所有行前的这个缩进给删掉就行了
查找每行的行前空白,主要利用了正则中的 m 参数
const leadingSpacesAtLine = str.match(/^(?!$)[ \t]*/gm);
然后使用 reduce 遍历,获取最小空白值
const minSapceNum = leadingSpacesAtLine.reduce((min, line) => {
return Math.min(
typeof min === "number" ? min : min.length,
line.length
);
});
所有行都删除最小空白值的空白,就实现了格式化缩进
return str.replace(new RegExp(`^[ \\t]{${minSapceNum}}`, "gm"), "");
startsWith && endsWith
经典函数,原生 js 的字符串方法中也有这两个方法,不过我这里处理了一下参数,支持对正则表达式的判断
startsWith("abc", "a"); // -> true
startsWith("abc", /^a/); // -> true
endsWith("abc", "c"); // -> true
endsWith("abc", /c$/); // -> true
"--------------------------"
如果是字符串类型,则直接调用 string 的方法并返回
if (typeof prefix === "string") {
return str.startsWith(prefix, startPosition);
}
如果是正则类型,实际就运行了一下 Regexp.test(string),不过内部对正则进行了一些加工
实际上 startsWith("abc", /a/) 等同于 startsWith("abc", /^a/), 两者实际运行的都是 /^a/.test("abc")
if (prefix instanceof RegExp) {
const newStr = str.slice(startPosition);
if (prefix.source.startsWith("^")) {
return prefix.test(newStr);
} else {
return new RegExp("^" + prefix.source, prefix.flags).test(newStr);
}
}
getTermLeft && getTermRight && getRangeByTerm
也是经典函数,就是获取字符串中指定位置的一串字符串,这里的参数也支持正则表达式进行判断
getTermLeft("abcdec", "c") // -> "ab"
getTermLeft("abcdec", "c", 2) // -> "abcde"
getTermLeft("abc1de2", /\d/) // -> "abc"
getTermLeft("abc1de2", /\d/, 2) // -> "abc1de"
...
"--------------------------"
使用正则表达式进行字符串切分
const grep = new RegExp(`.+?(?=${typeof searchTerm === "string" ? searchTerm : searchTerm.source})`, "g");
const result = str.match(grep);
对得到的结果根据第三个参数的值进行 slice 切分,切分后 join 转为字符串后返回。
return result.slice(0, beforeWhichTimes).join("");
"--------------------------"
getTermRight 也是同理,不过正则表达式要做些更改,代码中实例使用的是下面这个。
new RegExp(`(?<=(^|${searchGrep})).*?(${searchGrep}|$)`, "g")
"--------------------------"
getRangeByTerm("a1bcd2e", [/\d/, /\d/]) // -> "bcd"
getRangeByTerm 就不支持选择第几个参数了,仅支持设置一个左值和一个右值,然后返回中间值,实际使用的正则表达式如下
new RegExp(`${lGrep}((?:[\\s\\S](?<!${lGrep}))*?)${rGrep}`)
randomList
获取一个随机数列表
randomList(1, 10) // -> [1 ~ 10, ...*9]
randomList(1, 10, 5) // -> [1 ~ 10, ...*4]
randomList(1, 10, { count: 5, unique: true }) // -> [1 ~ 10, ...*4] (unique)
"--------------------------"
如果没有 unique 参数,或者 unique 值为 false,那么直接 push 想要的个数的 random 就行
if (!unique) {
const list = [];
for (let i = 0; i < count; i++) {
list.push(random(min, max));
}
return list;
} else {
如果需要 unique,这里有两个逻辑,如果 count 数量小,那就 push 到想要的个数
如果 count 数量过大,那就直接创建一个 [min, min + 1, ... , max - 1, max] 的数组,然后打乱再截取
if (count > (max - min) / 2) {
let randomArr = range(min, max);
randomArr = shuffle(randomArr);
return randomArr.slice(0, count);
} else {
const randomSet = new Set();
while (randomSet.size < count) {
randomSet.add(random(min, max));
}
return Array.from(randomSet);
}
}
fori
看名字就知道这是一个循环函数,原本想着原生的循环已经够用了,就不特意再写一个循环,结果本着方便还是简单实现了一下,因为有链式调用嘛(xd),为了方便,直接循环了 iterator, 所以可能并不严谨,
如果有 Symbol.iterator 属性或者有 Symbol.asyncIterator 属性代表可以循环
if (target[Symbol.iterator] || target[Symbol.asyncIterator]) {
let i = 0;
如果有 asyncIterator 参数,或者有 Symbol.asyncIterator 属性代表这是一个 异步循环,那么使用 for await of 进行循环,并返回一个 Promise
if (asyncIterator || target[Symbol.asyncIterator]) {
return new Promise((resolve, reject) => {
(async () => {
const resPromiseList = [];
for await (const iterator of target) {
resPromiseList.push(
new Promise((res) => {
res(iteratee.call(thisArg, iterator, i, target));
})
);
i++;
}
Promise.all(resPromiseList)
.then((v) => { resolve(v) })
.catch((e) => { reject(e) });
})();
});
} else {
如果是正常的实现了 Symbol.iterator 接口的对象,那么使用 for of 循环,然后返回一个数组
const resList = [];
for (const iterator of target) {
const res = iteratee.call(thisArg, iterator, i, target);
i++;
resList.push(res);
}
return resList;
}
}
因为 Object 没有实现 Symbol.iterator 接口,所以默认无法循环,这里对 Object 进行特殊处理,循环了 Object.entries(collection) 方法处理后的结果。
if (collection instanceof Object) {
results = loop(Object.entries(collection));
}
formatDate
普通的日期处理函数,当然如果对时间处理复杂一些还是推荐使用 day.js
formatDate("2023-01-01", "YYYY-MM-DD hh:mm:ss") // => '2023-01-01 00:00:00'
formatDate("2023-01-01")("[YYYYMD]YYYY-M-D h:m:s") // => 'YYYYMD2023-1-1 0:0:0'
formatDate("2023-01-01", "dddd") // => 'Sunday'
formatDate("2023-01-01", { format: "dddd", lang: "zh-CN" }) // => '星期日'
"--------------------------"
其它参数就是普通的匹配替换,这里简单说明一下对星期的处理
switch (match) {
case "d":
如果是 d,那么就直接返回数字
return week;
如果是 dd,ddd,dddd,那么使用 Intl.DateTimeFormat 进行进一步的处理
(Intl 为原生支持的一个用于国际化的 API, 它提供了精确的字符串对比、数字格式化,和日期时间格式化,具体可以去 MDN 上查看)
因为其它地方都是数字替换,使用只有星期进行了特殊处理
例如星期六,dd 会返回 六, ddd 会返回 周六,dddd会返回 星期六,分别对应了 weekday 的三种参数值。
case "dd":
return new Intl.DateTimeFormat(lang, {
calendar: lang.startsWith("zh") ? "chinese" : "iso8601",
weekday: "narrow"
}).format(willFormatDate);
case "ddd":
return new Intl.DateTimeFormat(lang, {
calendar: lang.startsWith("zh") ? "chinese" : "iso8601",
weekday: "short"
}).format(willFormatDate);
case "dddd":
return new Intl.DateTimeFormat(lang, {
calendar: lang.startsWith("zh") ? "chinese" : "iso8601",
weekday: "long"
}).format(willFormatDate);
}
其它
当然还有其它一些方法,这里就不做具体介绍了,大家可以去 github 上查看,或者访问 github page 都有更加详细的介绍。
希望大佬们可以用用,如果觉得不错的话可以点个 star
碎碎念
项目一开始是打算使用 ts 写的,但是因为个人能力不足,写着写着发现写 type 的时间远超于写代码的时间。所以就没使用 ts,不过所有方法都有 jsdoc 注释,虽然可能因为能力不足导致类型不太符合,但是不影响正常使用。