判断各种数据
ts
// cache toString
const toTagString = Object.prototype.toString;
const FuncToString = Function.prototype.toString;
// get value object tag like: [object Array]
export function getObjectTag(value: unknown): string {
return toTagString.call(value);
}
// value is object or not
export function isObject(value: unknown): boolean {
return value !== null && typeof value === 'object';
}
// value instance of Object or not
export function isPlainObject(value: unknown): boolean {
return getObjectTag(value) === '[object Object]';
}
// value is number or not
export function isNumber(value: unknown): boolean {
return typeof value === 'number';
}
// value is string or not
export function isString(value: unknown): boolean {
return typeof value === 'string';
}
// value is undefined or not
export function isUndefind(value: unknown): boolean {
return value === undefined;
}
// value is null or not
export function isNull(value: unknown): boolean {
return value === null;
}
// value is null or undefined
export function isNullable(value: unknown): boolean {
return Boolean(value === null || value === undefined);
}
// value is boolean or not
export function isBoolean(value: unknown): boolean {
return typeof value === 'boolean';
}
// value is symbol or not
export function isSymbol(value: unknown): boolean {
return typeof value === 'symbol';
}
// value is function or not
export function isFunction(value: unknown): boolean {
return typeof value === 'function';
}
export const isCallable = isFunction;
// value is isNative function or not
export function isNativeFunction(func: Function): boolean {
return isFunction(func) && FuncToString.call(func).includes('[native code]');
}
// value is promise like or not
export function isPromise(p: unknown): boolean {
return isObject(p) && isFunction(p.then);
}
// value is ES6 native promise or not
export function isNativePromise(p: unknown): boolean {
return isObject(p) && getObjectTag(p) === '[object Promise]';
}
// value instance of Date or not
export function isDate(value: unknown): boolean {
return getObjectTag(value) === '[object Date]';
}
// value instance of RegExp or not
export function isRegexp(value: unknown): boolean {
return getObjectTag(value) === '[object RegExp]';
}
// isMap & isWeakMap
export function isMap(value: unknown): boolean {
return isObject(value) && getObjectTag(value) === '[object Map]';
}
export function isWeakMap(value: unknown): boolean {
return isObject(value) && getObjectTag(value) === '[object WeakMap]';
}
// isSet & isWeakSet
export function isSet(value: unknown): boolean {
return isObject(value) && getObjectTag(value) === '[object Set]';
}
export function isWeakSet(value: unknown): boolean {
return isObject(value) && getObjectTag(value) === '[object WeakSet]';
}
// value is Element Node or not
export function isElement(value: unknown): boolean {
return isObject(value) && value.nodeType === 1 && !isPlainObject(value);
}
// is errors object, it's includes
// Error EvalError RangeError ReferenceError SyntaxError TypeError URIError
export function isError(value: unknown): boolean {
if (!isObject(value)) {
return false;
}
const tag = getObjectTag(value);
return Boolean(
tag === '[object Error]' ||
tag === '[object DOMException]' ||
(typeof value.message === 'string' && typeof value.name === 'string' && !isPlainObject(value)),
);
}
// is length property number or not
export function isLength(len: number) {
return len >= 0 && Number.isSafeInteger(len);
}
// isArray / isArrayLike
export const isArray = Array.isArray;
export function isArrayLike<T extends { length: number }>(value: T): boolean {
if (isFunction(value)) {
return false;
}
return isLength(value.length);
}
// value is File | Blob object or not
export function isFile(value: unknown) {
const tag = getObjectTag(value);
return tag === '[object Blob]' || tag === '[object File]';
}
// is empty Array/Object/Set/Map values
export function isEmpty(value: any): boolean {
if (isPlainObject(value)) {
return Object.keys(value).length === 0;
}
if (isString(value) || Array.isArray(value) || isArrayLike(value)) {
return value.length === 0;
}
if (isMap(value) || isSet(value) || isFile(value)) {
return value.size === 0;
}
return false;
}
// is primitive value or not
function isPrimitive(value) {
var primitiveTags = ['string', 'number', 'boolean', 'undefined', 'bigint', 'symbol'];
return value === null || primitiveTags.indexOf(typeofstr) !== -1;
}
// isNode
export function isNode() {
return getObjectTag(globalThis) === '[object global]' && isUndefind(window);
}
// isBrowser
export function isBrowser() {
return window && getObjectTag(window) === '[object Window]';
}
js
// cache toString
const toTagString = Object.prototype.toString;
const FuncToString = Function.prototype.toString;
// get value object tag like: [object Array]
export function getObjectTag(value) {
return toTagString.call(value);
}
// value is object or not
export function isObject(value) {
return value !== null && typeof value === 'object';
}
// value instance of Object or not
export function isPlainObject(value) {
return getObjectTag(value) === '[object Object]';
}
// value is number or not
export function isNumber(value) {
return typeof value === 'number';
}
// value is string or not
export function isString(value) {
return typeof value === 'string';
}
// value is undefined or not
export function isUndefind(value) {
return value === undefined;
}
// value is null or not
export function isNull(value) {
return value === null;
}
// value is null or undefined
export function isNullable(value) {
return Boolean(value === null || value === undefined);
}
// value is boolean or not
export function isBoolean(value) {
return typeof value === 'boolean';
}
// value is symbol or not
export function isSymbol(value) {
return typeof value === 'symbol';
}
// value is function or not
export function isFunction(value) {
return typeof value === 'function';
}
export const isCallable = isFunction;
// value is isNative function or not
export function isNativeFunction(func) {
return isFunction(func) && FuncToString.call(func).includes('[native code]');
}
// value is promise like or not
export function isPromise(p) {
return isObject(p) && isFunction(p.then);
}
// value is ES6 native promise or not
export function isNativePromise(p) {
return isObject(p) && getObjectTag(p) === '[object Promise]';
}
// value instance of Date or not
export function isDate(value) {
return getObjectTag(value) === '[object Date]';
}
// value instance of RegExp or not
export function isRegexp(value) {
return getObjectTag(value) === '[object RegExp]';
}
// isMap & isWeakMap
export function isMap(value) {
return isObject(value) && getObjectTag(value) === '[object Map]';
}
export function isWeakMap(value) {
return isObject(value) && getObjectTag(value) === '[object WeakMap]';
}
// isSet & isWeakSet
export function isSet(value) {
return isObject(value) && getObjectTag(value) === '[object Set]';
}
export function isWeakSet(value) {
return isObject(value) && getObjectTag(value) === '[object WeakSet]';
}
// value is Element Node or not
export function isElement(value) {
return isObject(value) && value.nodeType === 1 && !isPlainObject(value);
}
// is errors object, it's includes
// Error EvalError RangeError ReferenceError SyntaxError TypeError URIError
export function isError(value) {
if (!isObject(value)) {
return false;
}
const tag = getObjectTag(value);
return Boolean(
tag === '[object Error]' ||
tag === '[object DOMException]' ||
(typeof value.message === 'string' && typeof value.name === 'string' && !isPlainObject(value)),
);
}
// is length property number or not
export function isLength(len) {
return len >= 0 && Number.isSafeInteger(len);
}
// isArray / isArrayLike
export const isArray = Array.isArray;
export function isArrayLike(value) {
if (isFunction(value)) {
return false;
}
return isLength(value.length);
}
// value is File | Blob object or not
export function isFile(value) {
const tag = getObjectTag(value);
return tag === '[object Blob]' || tag === '[object File]';
}
// is empty Array/Object/Set/Map values
export function isEmpty(value) {
if (isPlainObject(value)) {
return Object.keys(value).length === 0;
}
if (isString(value) || Array.isArray(value) || isArrayLike(value)) {
return value.length === 0;
}
if (isMap(value) || isSet(value) || isFile(value)) {
return value.size === 0;
}
return false;
}
// is primitive value or not
function isPrimitive(value) {
var primitiveTags = ['string', 'number', 'boolean', 'undefined', 'bigint', 'symbol'];
return value === null || primitiveTags.indexOf(typeofstr) !== -1;
}
// isNode
export function isNode() {
return getObjectTag(globalThis) === '[object global]' && isUndefind(window);
}
// isBrowser
export function isBrowser() {
return window && getObjectTag(window) === '[object Window]';
}
字符串格式处理
ts
/**
* 驼峰式字符串
* @param str - 要处理的字符串
* @param firstUpper - 第一个字符是否大写
* @param separators - 分割符号
* @returns 处理后的字符串
*/
export function camelCase(str: string, firstUpper: boolean = false, separators: string[] = ['-', '_']): string {
if (str.length < 2) {
return firstUpper ? str : str.toUpperCase();
}
let res = '';
let shouldUpperCase = false;
for (let i = 0, len = str.length; i < len; i++) {
let char = str[i];
if (separators.includes(char)) {
shouldUpperCase = true;
continue;
}
if (shouldUpperCase || (i === 0 && firstUpper)) {
char = char.toUpperCase();
}
res += char;
shouldUpperCase = false;
}
return res;
}
/**
* 根据驼峰式字符串切分新的字符串
* @param str - 要处理的字符串
* @param separator - 切分符号
* @returns 处理完的字符串
*/
function splitCamelCase(str: string, separator: string): string {
if (separator.length > 1) {
separator = separator[0];
}
const isUpperCacse = (char: string) => char === char.toUpperCase();
let char;
let res = '';
for (let i = 0, len = str.length; i < len; i++) {
char = str[i];
if (isUpperCacse(char)) {
if (i > 0) {
res += separator;
}
res += char.toLowerCase();
} else {
res += char;
}
}
return res;
}
// 下划线 snake_case
export const snakeCase = (str: string): string => splitCamelCase(str, '_');
// 中横线 kebab-case
export const kebabCase = (str: string): string => splitCamelCase(str, '-');
数组分页
ts
/**
* Paging Arrays
* @params {array} array
* @params {number} size default 1
* @return {Array<any[]>} result
* @throw TypeError
*/
export function chunk<T>(array: T[], size: number = 1): Array<T[]> {
const chunkSize = size >> 0; // convert to number
const len = array.length;
if (len === 0 || chunkSize === 0) {
return [];
}
if (len <= chunkSize) {
return [array];
}
const pages = Math.ceil(len / chunkSize);
const result: T[][] = new Array(pages);
for (let i = 0; i < pages; i++) {
result[i] = array.slice(i * chunkSize, (i + 1) * chunkSize);
}
return result;
}
js
/**
* Paging Arrays
* @params {array} array
* @params {number} size default 1
* @return {Array<any[]>} result
* @throw TypeError
*/
export function chunk(array, size = 1) {
const chunkSize = size >> 0; // convert to number
const len = array.length;
if (len === 0 || chunkSize === 0) {
return [];
}
if (len <= chunkSize) {
return [array];
}
const pages = Math.ceil(len / chunkSize);
const result = new Array(pages);
for (let i = 0; i < pages; i++) {
result[i] = array.slice(i * chunkSize, (i + 1) * chunkSize);
}
return result;
}
防抖
ts
/**
* 函数防抖
* @param func - 要防止抖动的函数
* @param wait - 触发频率(等待时间)
* @param isImmediate - 第一次是否立即调用
* @param thisArg - 调用函数的时候 this 指向
* @returns
*/
export function debounce(
func: Function,
wait: number = 100,
isImmediate = false,
thisArg?: object,
): (...args: any[]) => void {
let timer: NodeJS.Timeout;
let shouldExecute = Boolean(isImmediate);
return function (...args: unknown[]): void {
timer && clearTimeout(timer);
if (shouldExecute) {
func.apply(thisArg, ...args);
shouldExecute = false;
} else {
timer = setTimeout(func.bind(thisArg, ...args), wait);
}
};
}
js
/**
* 函数防抖
* @param func - 要防止抖动的函数
* @param wait - 触发频率(等待时间)
* @param isImmediate - 第一次是否立即调用
* @param thisArg - 调用函数的时候 this 指向
* @returns
*/
export function debounce(func, wait = 100, isImmediate = false, thisArg = null) {
let timer;
let shouldExecute = Boolean(isImmediate);
return function (...args) {
timer && clearTimeout(timer);
if (shouldExecute) {
func.apply(thisArg, ...args);
shouldExecute = false;
} else {
timer = setTimeout(func.bind(thisArg, ...args), wait);
}
};
}
节流
ts
export function throttle(func: Function, wait: number = 100, thisArg?: object): (...args: any[]) => void {
let timer: NodeJS.Timeout;
let startTime = Date.now();
return function (...args: any[]): void {
timer && clearTimeout(timer);
const nowTime = Date.now();
if (startTime + wait >= nowTime) {
func.apply(thisArg, args);
} else {
timer = setTimeout(func.bind(thisArg, ...args), wait);
}
};
}
js
export function throttle(func, wait = 100, thisArg = null) {
let timer;
let startTime = Date.now();
return function (...args) {
timer && clearTimeout(timer);
const nowTime = Date.now();
if (startTime + wait >= nowTime) {
func.apply(thisArg, args);
} else {
timer = setTimeout(func.bind(thisArg, ...args), wait);
}
};
}
柯理化
js
function curry(fn, ...preArgs) {
const countArgs = fn.length;
return function (...args) {
const items = [].concat(...preArgs, ...args);
if (items.length < countArgs) {
return curry.call(this, fn, items);
} else {
return fn.apply(null, items);
}
};
}
遍历对象
ts
/**
* forIn 遍历对象
* @template T extends object - 要遍历的对象
* @template K extends keyof T - 对象所有的 key
* @param obj - 要遍历的对象
* @param callback - 遍历处理函数
* @param onlyOwner - 是否只遍历自身的属性
*/
export function forIn<T extends object, K extends keyof T>(
obj: T,
callback: (key: K, value: T[K], source: T) => void,
onlyOwner: boolean = true,
): void {
for (let key in obj) {
if (onlyOwner) {
/* @ts-ignore */
obj.hasOwnProperty(key) && callback(key, obj[key], obj);
} else {
/* @ts-ignore */
callback(key, obj[key], obj);
}
}
}
js
/**
* forIn 遍历对象
* @template T extends object - 要遍历的对象
* @template K extends keyof T - 对象所有的 key
* @param obj - 要遍历的对象
* @param callback - 遍历处理函数
* @param onlyOwner - 是否只遍历自身的属性
*/
export function forIn(obj, callback, onlyOwner = true) {
for (let key in obj) {
if (onlyOwner) {
/* @ts-ignore */
obj.hasownProperty(key) && callback(key, obj[key], obj);
} else {
/* @ts-ignore */
callback(key, obj[key], obj);
}
}
}
格式化日期对象
虽然有很多处理日期的库 moment.js 和 dayjs, 但是最基本的格式化还是得会
ts
/**
* 根据模板格式化字符串
* @param time - 要格式化的日期对象
* @param template - 格式化模板
* @returns 返回格式化后的时间字符串
*/
export function formatDate(time: Date, template: string): string {
let date: Date;
if (Object.prototype.toString.call(time) === '[object Date]') {
date = <Date>time;
} else {
throw new TypeError('[formateDate]time must be Date or string');
}
const year = date.getFullYear();
const month = date.getMonth() + 1;
const dates = date.getDate();
const hours = date.getHours();
const minutes = date.getMinutes();
const seconds = date.getSeconds();
const day = date.getDay() + 1;
const fillZero = (value: number): string => (value < 10 ? `0${value}` : String(value));
const templateMap = {
yy: year.toString().substring(0, 2),
yyyy: year,
mm: month,
MM: fillZero(month),
dd: dates,
DD: fillZero(dates),
hh: hours,
HH: fillZero(hours),
ii: minutes,
II: fillZero(minutes),
ss: seconds,
SS: fillZero(seconds),
ww: day,
};
let formated = template;
Object.keys(templateMap).forEach((key) => {
const value = String(templateMap[key]);
formated = formated.replace(key, value);
});
return formated;
}
js
/**
* 根据模板格式化字符串
* @param time - 要格式化的日期对象
* @param template - 格式化模板
* @returns 返回格式化后的时间字符串
*/
export function formatDate(time, template) {
let date;
if (Object.prototype.toString.call(time) === '[object Date]') {
date = time;
} else {
throw new TypeError('[formateDate]time must be Date or string');
}
const year = date.getFullYear();
const month = date.getMonth() + 1;
const dates = date.getDate();
const hours = date.getHours();
const minutes = date.getMinutes();
const seconds = date.getSeconds();
const day = date.getDay() + 1;
const fillZero = (value) => (value < 10 ? `0${value}` : String(value));
const templateMap = {
yy: year.toString().substring(0, 2),
yyyy: year,
mm: month,
MM: fillZero(month),
dd: dates,
DD: fillZero(dates),
hh: hours,
HH: fillZero(hours),
ii: minutes,
II: fillZero(minutes),
ss: seconds,
SS: fillZero(seconds),
ww: day,
};
let formated = template;
Object.keys(templateMap).forEach((key) => {
const value = String(templateMap[key]);
formated = formated.replace(key, value);
});
return formated;
}
获取随机字符串/颜色字符串
ts
/**
* get a random string id
* @return {string}
*/
export function randomString(): string {
return Math.random().toString(16).substring(2);
}
/**
* get a random hex color string
* @return {string}
*/
export function getRandomHexColor(): string {
return '#' + Math.floor(Math.random() * 0xffffff).toString(16);
}
js
/**
* get a random string id
* @return {string}
*/
export function randomString() {
return Math.random().toString(16).substring(2);
}
/**
* get a random hex color string
* @return {string}
*/
export function getRandomHexColor() {
return '#' + Math.floor(Math.random() * 0xffffff).toString(16);
}
获取一组数据的平均值
ts
/**
* 获取一组可迭代数据(number类型)的平均值,忽略不是数字的元素
* @param values - 可迭代数据
* @returns number
*/
export function meanOf(values: Iterable<number>): number {
let sum = 0;
let numbersTotal = 0;
for (const item of values) {
if (typeof item !== 'number') {
continue;
}
sum += item;
numbersTotal++;
}
return sum / numbersTotal;
}
js
/**
* 获取一组可迭代数据(number类型)的平均值,忽略不是数字的元素
* @param values - 可迭代数据
* @returns number
*/
export function meanOf(values) {
let sum = 0;
let numbersTotal = 0;
for (const item of values) {
if (typeof item !== 'number') {
continue;
}
sum += item;
numbersTotal++;
}
return sum / numbersTotal;
}
函数缓存
ts
interface KeyMakerFunc {
(...args: any[]): string;
}
interface MemorizedFunc {
cacheMap: Map<string, unknown>;
(...args: unknown[]): unknown;
}
/**
* 函数记忆
* @param func - 要缓存接过的函数
* @param keyMaker - 生成缓存 key 的函数
* @returns 返回缓存后的函数
*/
export function memorize(func: CallableFunction, keyMaker: KeyMakerFunc = JSON.stringify): MemorizedFunc {
const memorized: MemorizedFunc = (...args: unknown[]) => {
const key = keyMaker(args);
if (!memorized.cacheMap.has(key)) {
memorized.cacheMap.set(key, func(...args));
}
return memorized.cacheMap.get(key);
};
memorized.cacheMap = new Map();
return memorized;
}
js
/**
* 函数记忆
* @param func - 要缓存接过的函数
* @param keyMaker - 生成缓存 key 的函数
* @returns 返回缓存后的函数
*/
export function memorize(func, keyMaker = JSON.stringify) {
const memorized = (...args) => {
const key = keyMaker(args);
if (!memorized.cacheMap.has(key)) {
memorized.cacheMap.set(key, func(...args));
}
return memorized.cacheMap.get(key);
};
memorized.cacheMap = new Map();
return memorized;
}
只执行一次的函数
ts
/**
* 自会让函数调用一次
* @param func - 要调用的函数
* @param thisArg - func 调用时 this 的指向
* @returns 新的函数
*/
const caches = new WeakSet();
export function once(func: Function, thisArg?: object): Function {
return function (...args: unknown[]) {
if (caches.has(func)) {
return;
}
caches.add(func);
func.apply(thisArg, args);
};
}
js
/**
* 自会让函数调用一次
* @param func - 要调用的函数
* @param thisArg - func 调用时 this 的指向
* @returns 新的函数
*/
const caches = new WeakSet();
export function once(func, thisArg = null) {
return function (...args) {
if (caches.has(func)) {
return;
}
caches.add(func);
func.apply(thisArg, args);
};
}
获取对象指定 key 的值成为一个新值
ts
/**
* 在一个对象中挑选一个 key, 然后返回新对象 pick({a:1, b:2}, 'a', 'c')
* @param source - 源对象
* @param keys - 挑选的 key
* @returns 处理后的新对象
*/
export function pick<T extends object, K extends keyof T>(source: T, ...keys: K[]): Pick<T, K> {
const target: any = Object.create(null);
keys.forEach((key) => {
target[key] = source[key];
});
return target;
}
js
/**
* 在一个对象中挑选一个 key, 然后返回新对象 pick({a:1, b:2}, 'a', 'c')
* @param source - 源对象
* @param keys - 挑选的 key
* @returns 处理后的新对象
*/
export function pick(source, ...keys) {
const target = Object.create(null);
keys.forEach((key) => {
target[key] = source[key];
});
return target;
}
排除对象指定 key 的值返回一个新值
ts
export function exclude<T extends object, K extends keyof T>(source: T, ...keys: K[]): Exclude<T, K> {
const target: any = Object.create(null);
Object.keys(source).forEach((key) => {
/* @ts-ignore */
if (!keys.includes(key)) {
target[key] = source[key];
}
});
return target;
}
js
export function exclude(source, ...keys) {
const target = Object.create(null);
Object.keys(source).forEach((key) => {
if (!keys.includes(key)) {
target[key] = source[key];
}
});
return target;
}
返回指定范围的随机数
ts
/**
* 返回指定范围内的随机数
* @param min - 最小值
* @param max - 最大值
* @returns 返回值
*/
export function range(min: number, max: number): number {
return Math.floor(Math.random() * (max - min + 1) + min);
}
js
/**
* 返回指定范围内的随机数
* @param min - 最小值
* @param max - 最大值
* @returns 返回值
*/
export function range(min, max) {
return Math.floor(Math.random() * (max - min + 1) + min);
}
打乱数组元素顺序
ts
/**
* shuffle array items
* @param array
* @returns {Array}
*/
export function shuffle<T>(array: T[]): T[] {
if (array.length < 1) {
return [];
}
return [...array].sort(() => Math.random() - 0.5).reverse();
}
js
/**
* shuffle array items
* @param array
* @returns {Array}
*/
export function shuffle(array) {
if (array.length < 2) {
return [];
}
return [...array].sort(() => Math.random() - 0.5);
}
uuid
需要兼容低版本浏览器需要安装 uuid
ts
// 生成一个 uuid
import { v4 as uuidv4 } from 'uuid';
export function uuid(): string {
if (typeof window.crypto !== 'undefined') {
return window.crypto.randomUUID(); // v4
}
return uuidv4();
}
自增id
ts
let __id = 0;
export const $uid = () => ++__id;
圣杯模式继承
兼容老旧浏览器, 无法使用 class 语法, 一个继承写起来太费劲了
js
var inherit = (function () {
function Middleman() {}
return function (child, parent) {
Middleman.prototype = parent.prototype;
child.prototype = new Middleman();
child.prototype.constructor = child;
child.super_class = parent;
};
})();