```
npm install @zyzgroup/core-common``
将多参数函数替换成一个只接受 Thunk回调函数 作为参数的单参数函数
@example
fn(args1, args2, ... , callback) = thunkify(fn)(args1, args2, ... )(callback)
`
`
TypedArray 数组没有现成的concat方法
concateTypedArray(Uint8Array, Uint8Array.of(1, 2), Uint8Array.of(3, 4)); // Uint8Array [1, 2, 3, 4]
`
`
throttle 节流
ms 毫秒内只运行一次,若在 ms 毫秒内重复触发,只有一次生效
`
`
debounce 防抖
ms 毫秒后再执行该事件,若在 ms 毫秒内被重复触发,则重新计时
debounce 的问题在于它“太有耐心了”
试想,如果用户的操作十分频繁,它每次都不等 debounce 设置的 delay 时间结束就进行下一次操作,
于是每次 debounce 都为该用户重新生成定时器,回调函数被延迟了不计其数次。
频繁的延迟会导致用户迟迟得不到响应,用户同样会产生“这个页面卡死了”的观感。
`
`
异步串联
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));
`
`
@description 尾递归优化
const sum = tco(function(x, y) {
if (y > 0) {
return sum(x + 1, y - 1)
}
else {
return x
}
});
sum(1, 100000) // 100001
`
`
函数参数反转
`
`
缓存函数结果 cache(fn) 会使 fn 变为 异步
`
`
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
`
`
typeof 可以判断基础数据类型(null除外),但是引用数据类型中,除了 function 之外,其他的也无法判断
Undefined "undefined"
Boolean "boolean"
Number "number"
BigInt "bigint"
String "string"
Symbol "symbol"
Null "object"
Function "function"
array "object"
其他任何对象 "object"
`
`
isArrayLike([1, 2, 3]);
// => true
isArrayLike(document.body.children);
// => true
isArrayLike('abc');
// => true
isArrayLike(noop);
// => false
`
`
广泛意义的 object : Object + Array + Function + 非null
Checks if value is theObject
language type
of . (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
`
`
Performs a
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
`
`
获取对象方法的时候,自动绑定this
const logger = bindObjectThis(new Logger());
const { printName } = logger;
// printName 的 this 指向 logger
`
`
Variable to hold a counting semaphore 信号
- Incrementing adds a lock and puts the scheduler in a suspended state (if it's notreleased
already suspended)
- Decrementing releases a lock. Zero locks puts the scheduler in a state. Thissuspended
triggers flushing the queued tasks.
*/
let semaphore = 0;
/**
Puts the scheduler in a state. Scheduled tasks will be queued until the
scheduler is released.
`
`
Puts the scheduler in a released state.
`
`
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).
`
`
Releases the current lock. Executes all queued tasks if the scheduler is in the released state.
`
`
Executes or queues a task depending on the state of the scheduler (suspended or released)
`
`
Puts the scheduler in a suspended state and executes a task immediately.
`
`
Used to match RegExp{|}~^.-]+\@[A-Za-z0-9._-]+\.[A-Za-z0-9]+$/gm;
syntax characters.
/
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_!#$%&'*+/=?
// 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 =
/^\sdata:(?:[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.replace(/\s+/g, ""),
[\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}]
"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 = /(?
// "2015-01-02".replace(re, "$
// 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是两个码点,但是必须放在一起配对使用,代表字符𝌆
``
`
税号: 15/17/18/20位数字与大写字母组合
`
`
stringReplace("rgb({r},{g},{b})", { r: "255", g: "255", b: "255" })
`
`
rawfoo${1 + 2}bar // "foo3bar"
raw({ raw: ['foo', 'bar'] }, 1 + 2) // "foo3bar"
`
`
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")^${glue}
.toLowerCase();
v = v.replace(new RegExp(), "");
return v;
};
/**
toPascal("aaa_bbb-ccc") = AaaBbbCcc
toPascal("aaabbbccc") = Aaabbbccc
`
`
toCamel("aaa_bbb-ccc") = aaaBbbCcc
`
`
计算字符串所占的内存字节数,默认使用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
``