常用方法
getDataType
该方法用于获取参数数据类型,接收一个任意类型的参数,返回该参数的数据类型,小写字母形式。
import { getDataType } from '...'
getDataType('') // string
getDataType(1) // number
getDataType(true) // boolean
getDataType([]) // array
getDataType({}) // object方法源码
/**
* 获取传入数据的类型,返回小写字符串格式
*/
export const getDataType = (obj: unknown): string => {
let res = Object.prototype.toString.call(obj).split(' ')[1];
res = res.substring(0, res.length - 1).toLowerCase();
return res;
};debounce
该方法用于事件防抖,接收一个函数、一个延迟时间和一个是否立即执行一次的布尔值,返回一个新函数,新函数在延迟时间内多次调用时,只会执行最后一次调用。
import { debounce } from '...'
const fn = debounce(() => console.log('debounce'), 1000)
fn()
fn() // 1秒后输出 debounce方法源码
/**
* @description 防抖函数,一定时间内多次触发,只执行最后触发的一次,可能永远不会执行
* @param fn 需要防抖的函数
* @param delay 间隔时间,默认200ms,单位ms
* @param immediate 第一次是否立即执行,默认false
* @returns
*/
export const debounce = <T = unknown>(
fn: (...args: T[]) => void,
delay: number = 200,
immediate: boolean = false
) => {
let timer: NodeJS.Timeout | null = null;
let isFirst = true;
return function (this: any, ...args: T[]) {
timer && clearTimeout(timer);
if (immediate && isFirst) {
isFirst = false;
fn.apply(this, args);
return;
}
timer = setTimeout(() => {
fn.apply(this, args);
}, delay);
};
};throttle
该方法用于事件节流,接收一个函数和一个延迟时间,返回一个新函数,新函数在延迟时间内多次调用时,只会执行第一次调用。
import { throttle } from '...'
const fn = throttle(() => console.log('throttle'), 1000)
fn() // 立即输出 throttle
fn() // 间隔时间小于1s,不会执行方法源码
/**
* @description 节流函数,触发一次后,下次触发需要间隔一定的时间
* @param fn 需要执行的函数
* @param delay 间隔时间,默认1s,单位ms
* @param options
* @returns
*/
export const throttle = <T = unknown>(
fn: (...args: T[]) => void,
delay: number = 1000,
options: { leading?: boolean; trailing?: boolean } = { leading: true, trailing: false }
) => {
let timer: NodeJS.Timeout | null = null;
let previous = 0;
let context: any = null;
let argsCache: T[] | null = null;
const leading = options.leading !== undefined ? options.leading : true;
const trailing = options.trailing !== undefined ? options.trailing : false;
return function (this: any, ...args: T[]) {
const now = Date.now();
if (!previous && !leading) {
previous = now;
}
const remaining = delay - (now - previous);
context = this;
argsCache = args;
if (remaining <= 0 || remaining > delay) {
if (timer) {
clearTimeout(timer);
timer = null;
}
previous = now;
fn.apply(context, args);
argsCache = null;
} else if (!timer && trailing) {
timer = setTimeout(() => {
timer = null;
previous = leading ? Date.now() : 0;
if (argsCache) {
fn.apply(context, argsCache);
argsCache = null;
}
}, remaining);
}
};
};curry
柯理化方法,该方法接收一个函数,返回一个新函数,新函数接收的参数数量不足时,会返回一个新函数,直到参数数量足够执行原函数。
import { curry } from '...'
const add = curry((a: number, b: number, c: number) => a + b + c)
add(1)(2)(3) // 6
add(1, 2)(3) // 6
add(1)(2, 3) // 6方法源码
export interface ICurryBack<T = unknown, B = unknown> {
(...params: T[]): B | ICurryBack<T, B>;
}
/**
* 柯里化:接收一个函数,返回一个新函数,新函数接收的参数数量不足时,会返回一个新函数,直到参数数量足够执行原函数
*/
export const curry = <T = unknown, B = unknown>(
fn: Function,
...args: T[]
): B | ICurryBack<T, B> => {
const length = fn.length;
if (args.length < length) {
return function (...params: T[]) {
return curry(fn, ...args, ...params);
};
}
return fn(...args);
};compose
组合方法,多用于函数式编程,接收多个函数,返回一个新函数,新函数会将参数依次传入每个函数,并将结果传入下一个函数。
import { compose } from '...'
const fn = compose(
(a: number) => a + 1,
(a: number) => a * 2
)
fn(1) // 3方法源码
/**
* 函数式编程实现,从右往左执行,函数返回值会传给下一个执行的函数
*/
export const compose = <T = unknown>(...funcs: Function[]) => {
if (funcs.length === 0) {
return (arg: T) => arg;
}
if (funcs.length === 1) {
return funcs[0];
}
return funcs.reduce((a, b) => {
return (...args: T[]) => {
return a(b(...args));
};
});
};deepClone
深拷贝方法,接收一个对象,返回该对象的深拷贝,支持Map、Set、RegExp、Date、Function类型、循环引用与简单数据类型。
import { deepClone } from '...'
const obj = { a: 1, b: { c: 2 } }
const clone = deepClone(obj)
obj === clone // false
obj.b === clone.b // false方法源码
/**
* 深拷贝实现(支持Map、Set、RegExp、Date、Function类型和循环引用,不支持symbol属性)
*/
export const deepClone = <T = unknown>(obj: T) => {
/**
* 用来保存引用关系,解决循环引用问题
*/
const copyObj: any = {};
const clone = (data: any): T => {
if (!(data instanceof Object)) {
return data;
}
const newObj: any = Array.isArray(data) ? [] : {};
for (const key in data) {
if (!data.hasOwnProperty(key)) {
continue;
}
if (!(data[key] instanceof Object)) {
newObj[key] = data[key];
continue;
}
if (data[key] instanceof Date) {
newObj[key] = new Date(data[key].getTime());
continue;
}
if (data[key] instanceof RegExp) {
newObj[key] = new RegExp(data[key]);
continue;
}
if (data[key] instanceof Function) {
// 处理es6简写方法名的问题,例如:{hi() {return 1;}}
const funcStr = data[key].toString().replace(/^function/, '');
newObj[key] = new Function(`return function ${funcStr}`)();
continue;
}
if (data[key] instanceof Map) {
newObj[key] = new Map();
data[key].forEach((val: any, mapKey: any) => {
if (!(mapKey instanceof Object) && !(val instanceof Object)) {
newObj[key].set(mapKey, val);
} else {
newObj[key].set(clone(mapKey), clone(val));
}
});
continue;
}
if (data[key] instanceof Set) {
newObj[key] = new Set();
data[key].forEach((val: any) => {
if (!(val instanceof Object)) {
newObj[key].add(val);
} else {
newObj[key].add(clone(val));
}
});
continue;
}
// 判断是否为循环引用
if (copyObj[key] === data[key]) {
newObj[key] = data[key];
continue;
}
copyObj[key] = data[key];
newObj[key] = clone(data[key]);
}
return newObj;
};
return clone(obj);
};getQueryString
该方法接收一个对象与一个是否编码的布尔值,返回一个拼接的查询字符串。
import { getQueryString } from '...'
getQueryString({ a: 1, b: 2, c: null }) // ?a=1&b=2
getQueryString({ a: 1, b: 2, c: '哈哈' }) // ?a=1&b=2&c=%E5%93%88%E5%93%88方法源码
/**
* @description 拼接查询字符串
* @param data
* @param isEncode 是否使用encodeURIComponent()将值编码,默认true
* @returns
*/
export const getQueryString = (
data: Record<string, string | number | boolean | null | undefined>,
isEncode = true
) => {
let str = '?';
for (const key in data) {
if (data[key] === null || data[key] === '' || data[key] === undefined) {
continue;
}
const value = isEncode
? encodeURIComponent(data[key] as string | number | boolean)
: data[key];
str += `${key}=${value}&`;
}
return str.substring(0, str.length - 1);
};formatTime
该方法接收一个Date对象与一个格式字符串,返回格式化时间字符串。格式字符串,默认:yyyy/MM/dd HH:mm:ss。yy: 输出两位数的年份, h:输出12小时制,H:输出24小时制,M:月份,m:分钟,一位字母则不补零。
import { formatTime } from '...'
formatTime() // 2024/06/01 12:56:55
formatTime(undefined, 'yy-M-d H:mm:ss') // 24-6-1 12:59:12方法源码
/**
* @description 格式化时间
* @param {Date} [date] Date对象,默认当前时间
* @param {String} [format] 输出格式字符串,默认:yyyy/MM/dd HH:mm:ss。yy: 输出两位数的年份,
* h:输出12小时制,H:输出24小时制,M:月份,m:分钟,一位字母则不补零
* @returns {String}
*/
export const formatTime = (
date: Date = new Date(),
format: string = 'yyyy/MM/dd HH:mm:ss'
): string => {
if (!format) {
return format;
}
const strategies = {
yy: () => {
return date.getFullYear().toString().substring(2, 4);
},
yyyy: () => {
return date.getFullYear().toString();
},
M: () => {
const month = date.getMonth() + 1;
return month.toString();
},
MM: () => {
const month = date.getMonth() + 1;
return month.toString().padStart(2, '0');
},
d: () => {
return date.getDate().toString();
},
dd: () => {
return date.getDate().toString().padStart(2, '0');
},
h: () => {
let hours = date.getHours();
if (hours > 12) {
hours -= 12;
}
return hours.toString();
},
hh: () => {
let hours = date.getHours();
if (hours > 12) {
hours -= 12;
}
return hours.toString().padStart(2, '0');
},
H: () => {
return date.getHours().toString();
},
HH: () => {
return date.getHours().toString().padStart(2, '0');
},
m: () => {
return date.getMinutes().toString();
},
mm: () => {
return date.getMinutes().toString().padStart(2, '0');
},
s: () => {
return date.getSeconds().toString();
},
ss: () => {
return date.getSeconds().toString().padStart(2, '0');
}
};
type IKey = keyof typeof strategies;
const replaceFn = (val: string): string => {
let func = strategies[val as IKey];
if (func instanceof Function) {
return func();
}
func = strategies[val.toLowerCase() as IKey];
if (func instanceof Function) {
return func();
}
// 没有匹配的方法,返回原字符串
return val;
};
return format.replace(/([Yy]{2,4}|[M]+|[Dd]+|[Hh]+|[m]+|[Ss]+)/g, replaceFn);
};idleDetection
网页空闲监测方法,接收一个回调函数与一个空闲时间,返回一个IdleDetection对象,该对象有startDetection、stopDetection、restartDetection方法。
import { idleDetection } from '...'
const idle = idleDetection(() => console.log('idle'), 1000)
idle.startDetection() // 用户空闲1秒后输出 idle方法源码
/**
* 页面空闲检测
* @param callback
* @param timeout 时长,默认60s,单位:秒
* @returns
*/
export const idleDetection = (callback: () => void, timeout = 60) => {
let pageTimer: NodeJS.Timeout | undefined;
let beginTime = 0;
const onClearTimer = () => {
pageTimer && clearTimeout(pageTimer);
pageTimer = undefined;
};
const onStartTimer = () => {
onClearTimer();
beginTime = Date.now();
pageTimer = setTimeout(() => {
callback();
}, timeout * 1000);
};
const onPageVisibility = () => {
onClearTimer();
if (document.visibilityState !== 'visible') {
return;
}
const currentTime = Date.now();
if (currentTime - beginTime >= timeout * 1000) {
callback();
return;
}
pageTimer = setTimeout(() => {
callback();
}, timeout * 1000 - (currentTime - beginTime));
};
const startDetection = () => {
onStartTimer();
document.addEventListener('keydown', onStartTimer);
document.addEventListener('mousemove', onStartTimer);
document.addEventListener('visibilitychange', onPageVisibility);
};
const stopDetection = () => {
onClearTimer();
document.removeEventListener('keydown', onStartTimer);
document.removeEventListener('mousemove', onStartTimer);
document.removeEventListener('visibilitychange', onPageVisibility);
};
const restartDetection = () => {
startDetection();
stopDetection();
};
return {
startDetection,
stopDetection,
restartDetection
};
};deepFindItem
深度查找树形结构中的某个节点,接收一个数组对象、一个查找函数与一个子元素键名,返回查找到的第一个节点。
import { deepFindItem } from '...'
const arr = [{ a: 1, children: [{ a: 2 }] }]
deepFindItem(arr, (item) => item.a === 2, 'children') // { a: 2 }方法源码
/**
* @description 深度查找树形结构中的某个节点
* @param data 树形结构数据
* @param compare 查找条件函数,返回 true 时表示找到
* @param childrenKey 子节点的 key,默认 'children'
* @returns
*/
export const deepFindItem = <T extends Record<string, any>>(
data: T[],
compare: (value: T) => boolean,
childrenKey = 'children'
): T | undefined => {
const queue: T[] = [...data]
while (queue.length > 0) {
const item = queue.shift()!
if (compare(item)) {
return item
}
if (item[childrenKey]?.length) {
queue.push(...item[childrenKey])
}
}
return undefined
}deepMap
深度遍历树形结构,接收一个数组对象、一个映射函数与一个子元素键名,返回一个新的数组对象。映射函数接收一个参数:当前节点。
import { deepMap } from '...'
const arr = [{ a: 1, b: 2, children: [{ a: 2, b: 4 }]}]
const newArr = deepMap(arr, (v) => {
return {
a: v.a * 2,
b: v.b * 2,
children: v.children
}
}) // result: [{ a: 2, b: 4, children: [{ a: 4, b: 8 }]}]方法源码
/**
* @description 深度遍历树形结构
* @param data 树形结构数据
* @param callback 回调函数,接收当前节点
* @param childrenKey 子节点的 key,默认 'children'
* @returns 返回新的树形结构数据
*/
export const deepMap = <T extends Record<any, any>>(
data: T[],
callback: (value: T) => T,
childrenKey = 'children'
): T[] => {
const result: T[] = []
const queue: { node: T; parent: T | null; index: number }[] = data.map((node, index) => ({
node,
parent: null,
index
}))
while (queue.length > 0) {
const { node, parent, index } = queue.shift()!
const newNode = callback({ ...node })
if (parent === null) {
result[index] = newNode
} else {
parent[childrenKey][index] = newNode
}
if (Array.isArray(newNode[childrenKey]) && newNode[childrenKey].length > 0) {
queue.push(
...newNode[childrenKey].map((child: T, idx: number) => ({
node: child,
parent: newNode,
index: idx
}))
)
}
}
return result
}deepForeach
树结构的广度优先遍历,接收一个数组对象、一个遍历函数与一个子元素键名。遍历函数接收一个参数:当前节点。
import { deepForeach } from '...'
const arr = [{ a: 1, b: 2, children: [{ a: 2, b: 4 }]}]
deepForeach(arr, (v) => {
console.log(v) // { a: 1, b: 2, children: [...] } => { a: 2, b: 4 }
})方法源码
/**
* @description 树结构的广度优先遍历
* @param data 树形结构数据
* @param callback 回调函数,接收当前节点
* @param childrenKey 子节点的 key,默认 'children'
*/
export const deepForeach = <T extends Record<string, any>>(
data: T[],
callback: (value: T) => void,
childrenKey = 'children'
) => {
if (!data?.length) return
const queue: T[] = [...data]
while (queue.length > 0) {
const node = queue.shift()!
callback(node)
if (Array.isArray(node[childrenKey]) && node[childrenKey].length > 0) {
queue.push(...node[childrenKey])
}
}
}getSystemTheme
该方法用于获取系统主题,返回字符串light或dark。
import { getSystemTheme } from '...'
const theme = getSystemTheme()方法源码
export const getSystemTheme = () => {
if (!window.matchMedia) {
const date = new Date();
const hours = date.getHours();
if (hours >= 7 && hours < 19) {
return 'light';
}
return 'dark';
}
const systemTheme = window.matchMedia('(prefers-color-scheme: dark)');
const theme = systemTheme.matches ? 'dark' : 'light';
return theme;
};followSystemTheme
该方法用于自动跟随系统主题,接收一个回调函数参数。系统主题变化时,调用回调并传入当前主题。
import { followSystemTheme } from '...'
const theme = followSystemTheme((theme) => {
console.log(theme) // 'light' / 'dark'
})方法源码
export const followSystemTheme = (
callback: (theme: 'dark' | 'light') => void
) => {
if (!window.matchMedia) {
const date = new Date();
const hours = date.getHours();
const time = date.getTime();
let delay = 0;
let mode: 'light' | 'dark' = 'light';
if (hours >= 0 && hours < 7) {
const nextTime = date.setHours(7);
delay = nextTime - time;
} else if (hours >= 7 && hours < 19) {
const nextTime = date.setHours(19);
delay = nextTime - time;
mode = 'dark';
} else {
const nextTime = date.setHours(23, 59, 59, 999) + 7 * 60 * 60 * 1000;
delay = nextTime - time;
}
setTimeout(() => {
callback(mode);
}, delay);
return;
}
const systemTheme = window.matchMedia('(prefers-color-scheme: dark)');
systemTheme.addEventListener('change', (e) => {
const theme = e.matches ? 'dark' : 'light';
callback(theme);
});
};