@zyzgroup/core-common
v0.2.13
Published
```
Downloads
284
Readme
API 文档(自动生成)
src/async.ts
thunkify( fn[任意函数]: variadicFn ) => (...args: any[]) => thunkFn
将多参数函数替换成一个只接受 Thunk回调函数 作为参数的单参数函数
@example
fn(args1, args2, ... , callback) = thunkify(fn)(args1, args2, ... )(callback)
src/buffer.ts
concateTypedArray()
TypedArray 数组没有现成的concat方法
concateTypedArray(Uint8Array, Uint8Array.of(1, 2), Uint8Array.of(3, 4)); // Uint8Array [1, 2, 3, 4]
src/fp.ts
throttle()
throttle 节流
ms 毫秒内只运行一次,若在 ms 毫秒内重复触发,只有一次生效
debounce()
debounce 防抖
ms 毫秒后再执行该事件,若在 ms 毫秒内被重复触发,则重新计时
debounce 的问题在于它“太有耐心了”
试想,如果用户的操作十分频繁,它每次都不等 debounce 设置的 delay 时间结束就进行下一次操作,
于是每次 debounce 都为该用户重新生成定时器,回调函数被延迟了不计其数次。
频繁的延迟会导致用户迟迟得不到响应,用户同样会产生“这个页面卡死了”的观感。
trampoline()
异步串联
const addA = (value, next) => {
next(value + 'A', 'a');
};
const addB = (value, anotherValue, next) => {
console.log(anotherValue); // => a
next(value + 'B');
};
const consoleLog = (value, next) => {
console.log(value);
};
pipeCallback(addA, addB, consoleLog)('1'); // 1AB
/
export const pipeCallback = (...fns: variadicFn[]): variadicFn => {
if (fns.length === 0) {
return (...args: any[]) => args;
}
if (fns.length === 1) {
return fns[0];
}
return fns.reduceRight(
(next, fn) =>
(...args) =>
fn(...args, next),
noop
);
};
/**
蹦床函数,循环调用返回函数的函数,将递归执行转为循环执行,适用于没有尾递归优化的环境
原递归函数
function sum(x, y) {
if (y > 0) {
return sum( x + 1, y - 1);
} else {
return x;
}
}
改造后的递归函数,返回函数
function sum(x, y) {
if (y > 0) {
return sum.bind(null, x + 1, y - 1); // 这里是返回一个函数,而不是函数里面调用函数,从而避免递归执行
} else {
return x;
}
}
trampoline(sum(1,1000000));
tco()
@description 尾递归优化
const sum = tco(function(x, y) {
if (y > 0) {
return sum(x + 1, y - 1)
}
else {
return x
}
});
sum(1, 100000) // 100001
reverseArgs()
函数参数反转
cache()
缓存函数结果 cache(fn) 会使 fn 变为 异步
tryEach()
Given an array of async functions, return a function that takes the same argument as each function
in the array. Each function is executed in series and the first that doesn't crash, returns the
value
src/object.ts
getType()
typeof 可以判断基础数据类型(null除外),但是引用数据类型中,除了 function 之外,其他的也无法判断
Undefined "undefined"
Boolean "boolean"
Number "number"
BigInt "bigint"
String "string"
Symbol "symbol"
Null "object"
Function "function"
array "object"
其他任何对象 "object"
isArrayLike()
isArrayLike([1, 2, 3]);
// => true
isArrayLike(document.body.children);
// => true
isArrayLike('abc');
// => true
isArrayLike(noop);
// => false
isObject()
广泛意义的 object : Object + Array + Function + 非null
Checks if `value` is the
[language type](http://www.ecma-international.org/ecma-262/7.0/#sec-ecmascript-language-types)
of `Object`. (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`)
isObject({});
// => true
isObject([1, 2, 3]);
// => true
isObject(noop);
// => true
isObject(null);
// => false
eq()
Performs a
[`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero)
comparison between two values to determine if they are equivalent.
let object = { 'a': 1 };
let other = { 'a': 1 };
eq(object, object);
// => true
eq(object, other);
// => false
eq('a', 'a');
// => true
eq('a', Object('a'));
// => false
eq(NaN, NaN);
// => true
bindObjectThis()
获取对象方法的时候,自动绑定this
const logger = bindObjectThis(new Logger());
const { printName } = logger;
// printName 的 this 指向 logger
src/scheduler.ts
suspend()
Variable to hold a counting semaphore 信号
- Incrementing adds a lock and puts the scheduler in a `suspended` state (if it's not
already suspended)
- Decrementing releases a lock. Zero locks puts the scheduler in a `released` state. This
triggers flushing the queued tasks.
*/
let semaphore = 0;
/**
Puts the scheduler in a `suspended` state. Scheduled tasks will be queued until the
scheduler is released.
release()
Puts the scheduler in a `released` state.
exec()
Executes a task 'atomically'. Tasks scheduled during this execution will be queued
and flushed after this task has finished (assuming the scheduler endup in a released
state).
flush()
Releases the current lock. Executes all queued tasks if the scheduler is in the released state.
asap()
Executes or queues a task depending on the state of the scheduler (`suspended` or `released`)
immediately()
Puts the scheduler in a `suspended` state and executes a task immediately.
src/string.ts
getUnicodeLengthES5()
Used to match `RegExp`
[syntax characters](http://ecma-international.org/ecma-262/7.0/#sec-patterns).
/
regexp = /[\\^$.*+?()[\]{}|]/g;
break;
case "regexp-flags":
regexp = /\w*$/;
break;
case "native-method":
regexp = RegExp(
"^" +
Function.prototype.toString
.call(Object.prototype.hasOwnProperty)
.replace(getDefinedRegExpPattern("regexp")!, "\\$&")
.replace(
/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g,
"$1.*?"
) +
"$"
);
break;
case "svg-segment":
regexp = /([mlhvzaqtcs])([^mlhvzaqtcs]*)/gi;
break;
case "mobile-china":
// https://ihateregex.io/expr/phone/
// regexp = /^\+?[1-9]{1}[0-9]{10}$/;
// 11位数字,以1开头,不能包含其他符号
regexp = /^[1]{1}[0-9]{10}$/;
// regexp = /^[+]?[(]?[0-9]{3}[)]?[-\s.]?[0-9]{3}[-\s.]?[0-9]{4,6}$/gi;
break;
case "email":
regexp =
/^[A-Za-z0-9_!#$%&'*+/=?`{|}~^.-]+\@[A-Za-z0-9._-]+\.[A-Za-z0-9]+$/gm;
// regexp =
// /^\w+((-\w+)|(\.\w+))*\@[A-Za-z0-9]+((\.|-)[A-Za-z0-9]+)*\.[A-Za-z0-9]+$/gm;
break;
case "ip":
regexp =
/^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/;
break;
case "url":
regexp = /^http[s]?:\/\/([\w-]+\.)+[\w-]+([\w-./?%&=]*)?$/;
break;
// case "relative-url":
// regexp = /^([a-z]+:)?[\\/]/i;
// break;
case "element-url":
regexp = /^url\(['"]?(.+?)['"]?\)$/i;
break;
case "date":
regexp = /^\d{4}(\-|\/|\.)(\d{1,2})(\-|\/|\.)(\d{1,2})$/;
break;
case "time":
regexp = /^(\d{1,2}):(\d{1,2}):(\d{1,2})$/;
break;
case "datetime":
regexp =
/^\d{4}(\-|\/|\.)(\d{1,2})(\-|\/|\.)(\d{1,2})\s+(\d{1,2}):(\d{1,2}):(\d{1,2})$/;
break;
case "base64-data":
regexp =
/^\s*data:(?:[a-z]+\/[a-z0-9-+.]+(?:;[a-z-]+=[a-z0-9-]+)?)?(?:;base64)?,([a-z0-9!$&',()*+;=\-._~:@/?%\s]*?)\s*$/i;
break;
case "hex-color":
regexp = /^#([0-9A-F]{3}|[0-9A-F]{4}|[0-9A-F]{6}|[0-9A-F]{8})$/i;
break;
case "color":
regexp =
/^\s*((#[a-f\d]{6})|(#[a-f\d]{3})|rgba?\(\s*([\d\.]+%?\s*,\s*[\d\.]+%?\s*,\s*[\d\.]+%?(?:\s*,\s*[\d\.]+%?)?)\s*\)|hsba?\(\s*([\d\.]+(?:deg|\xb0|%)?\s*,\s*[\d\.]+%?\s*,\s*[\d\.]+(?:%?\s*,\s*[\d\.]+)?)%?\s*\)|hsla?\(\s*([\d\.]+(?:deg|\xb0|%)?\s*,\s*[\d\.]+%?\s*,\s*[\d\.]+(?:%?\s*,\s*[\d\.]+)?)%?\s*\))\s*$/i;
break;
case "whitespace":
regexp =
/[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u2028\u2029\u202f\u205f\u3000]/g;
break;
case "commaSpaces":
regexp =
/[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029]*,[\x09\x0a\x0b\x0c\x0d\x20\xa0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029]*/;
break;
case "var-name":
regexp = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/;
break;
case "bezier":
regexp = /^(?:cubic-)?bezier\(([^,]+),([^,]+),([^,]+),([^\)]+)\)/;
break;
case "chinese-name":
regexp = /^(?:[\u4e00-\u9fa5·]{2,16})$/;
break;
case "chinese":
// regexp = /^[\u4E00-\u9FFF]+$/g;
// regexp = /^[\u4E00-\u9FA5\uF900-\uFA2D]+$/g;
regexp = new RegExp(
String.raw`
[\u{FA0E}\u{FA0F}\u{FA11}\u{FA13}\u{FA14}\u{FA1F}\u{FA21}\u{FA23}\u{FA24}\u{FA27}-\u{FA29}]
|[\u{4E00}-\u{9FCC}]
|[\u{3400}-\u{4DB5}]
|[\u{20000}-\u{2A6D6}]
|[\u{2A700}-\u{2B734}]
|[\u{2B740}-\u{2B81D}]
|[\u{2B820}-\u{2CEAF}]
|[\u{2CEB0}-\u{2EBEF}]
`.replace(/\s+/g, ""),
"u"
);
break;
case "mime-image":
regexp = /^image\//;
break;
case "mime-video":
regexp = /^video\//;
break;
case "mime-audio":
regexp = /^audio\//;
break;
case "mime-document-word":
regexp =
/^application\/(?:vnd\.openxmlformats-officedocument\.wordprocessingml\.document|msword|vnd\.ms-word\.document\.macroenabled\.12|vnd\.openxmlformats-officedocument\.wordprocessingml\.template|vnd\.ms-word\.template\.macroenabled\.12)$/;
break;
case "mime-document-excel":
regexp =
/^application\/(?:vnd\.openxmlformats-officedocument\.spreadsheetml\.sheet|vnd\.ms-excel|vnd\.ms-excel\.sheet\.macroenabled\.12|vnd\.openxmlformats-officedocument\.spreadsheetml\.template|vnd\.ms-excel\.template\.macroenabled\.12)$/;
break;
case "mime-document-ppt":
regexp =
/^application\/(?:vnd\.ms-powerpoint|vnd\.openxmlformats-officedocument\.presentationml\.presentation|vnd\.ms-powerpoint\.presentation\.macroenabled\.12|vnd\.openxmlformats-officedocument\.presentationml\.template|vnd\.ms-powerpoint\.template\.macroenabled\.12)$/;
break;
case "mime-document-json":
regexp = /^application\/json$/;
break;
case "mime-document-xml":
regexp = /^(?:application|text)\/(?:xml|xhtml\+xml)$/;
break;
}
return regexp;
};
export function getRegExpFlags(regExp: RegExp): string {
if (regExp.flags) {
return regExp.flags;
}
const flags = [];
regExp.global && flags.push("g");
regExp.ignoreCase && flags.push("i");
regExp.multiline && flags.push("m");
regExp.sticky && flags.push("y");
regExp.unicode && flags.push("u");
return flags.join("");
}
export function cloneRegExp(regExp: RegExp): RegExp {
const result = new RegExp(regExp.source, getRegExpFlags(regExp));
result.lastIndex = regExp.lastIndex;
return result;
}
export type TRegExpResult = {
// 匹配index
matchIndex: number;
// 完全匹配结果
fullMatch: string | null;
// 分组匹配结果
groupMatchs: string[];
};
// const re = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/u;
// "2015-01-02".replace(re, "$<day>/$<month>/$<year>"); // '02/01/2015'
// str.replace(/(john) (smith)/i, "$2, $1"); // Smith, John
// str.replace(/([a-z]+)\(?([^)a-z]+)\)?/gi, function (all, command, values) {
// values = values.split(/\s*,\s*/);
// }
// function replacer(match, p1, p2, /* …, */ pN, offset, string, groups) {
// return replacement;
// }
export const regTest = (s: string | RegExp, source: string): boolean => {
if (typeof s === "string") {
s = getDefinedRegExpPattern(s) || new RegExp(s);
}
return s.test(source);
};
// -1: not found
export const regSearchIndex = (s: string | RegExp, source: string): number => {
if (typeof s === "string") {
s = getDefinedRegExpPattern(s) || new RegExp(s);
}
return source.search(s);
};
export const regSplit = (s: string | RegExp, source: string): string[] => {
if (typeof s === "string") {
s = getDefinedRegExpPattern(s) || new RegExp(s);
}
return source.split(s);
};
/// - 如果 regexp 不带有 `g` 标记,则它以数组的形式返回第一个匹配项,其中包含分组和属性 `index`(匹配项的位置)、`input`(输入字符串,等于 str)
/// - 如果 regexp 带有 `g` 标记,则它将所有匹配项的数组作为字符串返回,而不包含分组和其他详细信息
/// - 如果没有匹配项,则无论是否带有标记 `g` ,都将返回 null
export const regMatch = (
s: string | RegExp,
source: string
): TRegExpResult[] | TRegExpResult | null => {
if (typeof s === "string") {
s = getDefinedRegExpPattern(s) || new RegExp(s);
}
const matchs = source.match(s);
if (!matchs) {
return null;
}
if (s.flags.toLowerCase().indexOf("g") >= 0) {
return matchs.map(
(m) =>
({
matchIndex: -1,
fullMatch: m,
groupMatchs: []
} as TRegExpResult)
);
}
return {
matchIndex: typeof matchs.index === "number" ? matchs.index : -1,
fullMatch: matchs[0],
groupMatchs: matchs.slice(1)
} as TRegExpResult;
};
/// 返回一个包含所有匹配正则表达式的结果及分组捕获组的迭代器(iterator)
export const regMatchAll = (
s: string | RegExp,
source: string
): TRegExpResult[] => {
if (typeof s === "string") {
s = getDefinedRegExpPattern(s) || new RegExp(s);
}
const results: TRegExpResult[] = [];
const matchsArray = source.matchAll(s);
for (const matchs of matchsArray) {
results.push({
matchIndex: typeof matchs.index === "number" ? matchs.index : -1,
fullMatch: matchs[0],
groupMatchs: matchs.slice(1)
} as TRegExpResult);
}
return results;
};
// - 返回字符串 str 中的 regexp 匹配项,它返回一个数组,未匹配到则返回 null
// - 如果没有 `g`,返回的第一个匹配与 `str.match(regexp)` 完全相同
// - 如果有标记 `g`,会返回第一个匹配项,并将紧随其后的位置保存在属性`regexp.lastIndex` 中。 下一次同样的调用会从位置 `regexp.lastIndex` 开始搜索,返回下一个匹配项,并将其后的位置保存在 `regexp.lastIndex` 中
export const regExec = (
s: string | RegExp,
source: string
): TRegExpResult[] | TRegExpResult | null => {
if (typeof s === "string") {
s = getDefinedRegExpPattern(s) || new RegExp(s);
}
if (s.flags.toLowerCase().indexOf("g") < 0) {
return regMatch(s, source);
}
const results: TRegExpResult[] = [];
let matchs;
while ((matchs = s.exec(source))) {
results.push({
matchIndex: typeof matchs.index === "number" ? matchs.index : -1,
fullMatch: matchs[0],
groupMatchs: matchs.slice(1)
} as TRegExpResult);
}
return results;
};
// 正确返回字符串长度的函数,返回码点长度(emoji 友好), 支持 UTF-16,4字节32位unicode
export function stringLength(text: string) {
const result = text.match(/[\s\S]/gu);
return result ? result.length : 0;
}
// 正确返回字符串长度的函数,返回码点长度(emoji 友好), 支持 UTF-16,4字节32位unicode
export function stringLength2(str: string) {
return [...str].length;
}
// 正确返回字符串长度的函数,返回码点长度(emoji 友好), 支持 UTF-16,4字节32位unicode
export function stringLength3(str: string) {
// Array.from 会按码点拆分
return Array.from(str).length;
}
/**
获取字符串长度,支持4字节32位unicode
UTF-8 标准规定,0xD800到0xDFFF之间的码点,不能单独使用,必须配对使用。
比如,\uD834\uDF06是两个码点,但是必须放在一起配对使用,代表字符𝌆
isChineseTaxNo()
税号: 15/17/18/20位数字与大写字母组合
stringReplace()
stringReplace("rgb({r},{g},{b})", { r: "255", g: "255", b: "255" })
raw()
raw`foo${1 + 2}bar` // "foo3bar"
raw({ raw: ['foo', 'bar'] }, 1 + 2) // "foo3bar"
toPascal()
toKebab("-AaaBbbCcc") = aaa-bbb-ccc
toKebab("Content-Type") = content-type
/
export const toKebab = (value: string, glue = "-"): string => {
let v = value
.replace(new RegExp(`${glue}?([A-Z])`, "g"), glue + "$1")
.toLowerCase();
v = v.replace(new RegExp(`^${glue}`), "");
return v;
};
/**
toPascal("aaa_bbb-ccc") = AaaBbbCcc
toPascal("aaabbbccc") = Aaabbbccc
toCamel()
toCamel("aaa_bbb-ccc") = aaaBbbCcc
stringByteSize()
计算字符串所占的内存字节数,默认使用UTF-8的编码方式计算,也可制定为UTF-16
UTF-8 是一种可变长度的 Unicode 编码格式,使用一至四个字节为每个字符编码
000000 - 00007F(128个代码) 0zzzzzzz(00-7F) 一个字节
000080 - 0007FF(1920个代码) 110yyyyy(C0-DF) 10zzzzzz(80-BF) 两个字节
000800 - 00D7FF
00E000 - 00FFFF(61440个代码) 1110xxxx(E0-EF) 10yyyyyy 10zzzzzz 三个字节
010000 - 10FFFF(1048576个代码) 11110www(F0-F7) 10xxxxxx 10yyyyyy 10zzzzzz 四个字节
注: Unicode在范围 D800-DFFF 中不存在任何字符
http://zh.wikipedia.org/wiki/UTF-8
UTF-16 大部分使用两个字节编码,编码超出 65535 的使用四个字节
000000 - 00FFFF 两个字节
010000 - 10FFFF 四个字节
http://zh.wikipedia.org/wiki/UTF-16
