手写一些工具函数
订阅发布模型
/**
* 事件触发器
* @mode 订阅发布模型
*/
export class EventEmitter {
private handlers: { [key: string]: Function[] } = {};
/**
* 订阅事件
* @param eventName string
* @param handler Function
* @example eventEmitter.on('event1', handler);
*/
on(eventName: string, handler: Function) {
if (!this.handlers[eventName]) {
this.handlers[eventName] = [];
}
this.handlers[eventName].push(handler);
}
/**
* 发布事件
* @param eventName string
* @param args any[]
* @example eventEmitter.emit('event1');
*/
emit(eventName: string, ...args: any[]) {
const handlers = this.handlers[eventName];
if (handlers && handlers.length) {
handlers.forEach((handler) => handler.apply(this, args));
}
}
/**
* 取消订阅
* @param eventName string
* @param handler Function
* @example emitter.off('event1', handler) // 取消事件 event1 全部订阅
* emitter.off('event1'); // 取消事件 event1 全部订阅
* emitter.off('*'); // 取消全部事件订阅
*/
off(eventName: string, handler?: Function) {
const handlers = this.handlers[eventName];
if (eventName === '*') {
this.handlers = {}
} else if (!handler) {
delete this.handlers[eventName]
} else if (handlers && handlers.length) {
const index = handlers.indexOf(handler);
if (index !== -1) {
handlers.splice(index, 1);
}
}
}
}
防抖函数
/**
* 防抖函数可以限制一个函数的调用频率,即在一定时间内只能调用一次
* @param callback - 需要被防抖的函数
* @param delay - 限制函数调用频率的时间间隔
* @returns 被防抖处理后的函数
* @example const debouncedFunc = debounce(myFunction, 1000);
* debouncedFunc(); // myFunction 将不会在这次调用时执行,因为距离上一次调用不到 1000 毫秒
* debouncedFunc(); // myFunction 将不会在这次调用时执行,因为距离上一次调用不到 1000 毫秒
*
* 防抖函数和节流函数都可以限制一个函数的调用频率,但是它们的实现方式不同。
* 防抖函数会在函数被调用后,等待一定时间(即delay参数指定的时间),如果在这段时间内函数再次被调用,那么计时器就会被重置,重新等待delay时间。只有在delay时间内没有再次调用函数,才会执行函数。这种方式适用于一些需要等待用户停止操作后才执行的函数,比如搜索框输入联想。
* 节流函数则是在一定时间内只能调用一次函数。如果在这段时间内函数再次被调用,那么函数不会被执行,直到时间间隔超过delay参数指定的时间。这种方式适用于一些需要限制调用频率的函数,比如滚动事件。
*/
export function debounce(callback: Function, delay: number): (...args: any[]) => void {
let timer: number;
return function (this: any, ...args: any[]) {
clearTimeout(timer);
timer = setTimeout(() => {
callback.apply(this, args);
}, delay);
};
}
节流函数
/**
* 节流函数可以限制一个函数的调用频率,即在一定时间内只能调用一次
* @param callback - 需要被节流的函数
* @param delay - 限制函数调用频率的时间间隔
* @returns 被节流处理后的函数
* @example const throttledFunc = throttle(myFunction, 1000);
* throttledFunc(); // myFunction 将会在第一次调用时立即执行
* throttledFunc(); // myFunction 将不会在这次调用时执行,因为距离上一次调用不到 1000 毫秒
*/
export function throttle(callback: Function, delay: number): (...args: any[]) => void {
let lastTime = 0;
return function (this: any, ...args: any[]) {
const now = new Date().getTime();
if (now - lastTime >= delay) {
lastTime = now;
callback.apply(this, args);
}
};
}
一些 DOM 操作
const DOM = window.document
/**
* 获取绝对链接地址
* @param path
*/
export const getAbsoluteUrl = (path: string): string => {
const anchor: HTMLAnchorElement = <HTMLAnchorElement>DOM.createElement('A')
anchor.href = path
return anchor.href
}
/**
* 设置页面标题
* @param title {string}
*/
export const setTitle = (title: string): void => {
DOM.title = title
}
/**
* 设置页面 REM 布局
* @param designWidth 设计宽
* @param split 分宽数
*/
export const setREMLayout = (designWidth: number = 750, split: number = 100, isSetViewport = true): ((pixel: number) => string) => {
const DOC_ELE = <HTMLElement>DOM.documentElement
let dprDesignWidth = designWidth
if (isSetViewport) {
const DPR = window.devicePixelRatio || 1
dprDesignWidth *= DPR
const SCALE = (1 / DPR).toFixed(2)
// Set "viewport" to HD display
let viewport = DOM.querySelector('meta[name="viewport"]')
if (!viewport) {
viewport = DOM.createElement('meta')
viewport.setAttribute('name', 'viewport')
}
viewport.setAttribute('content', `width=device-width, initial-scale=${SCALE}, maximum-scale=${SCALE}, minimum-scale=${SCALE}, user-scalable=0`)
// Set "data-dpr" for css hack
DOC_ELE.setAttribute('data-dpr', <string><any>DPR)
// Set body's fontSize to default font normal
DOM.body.style.fontSize = 16 * DPR + 'px'
}
const IDEAL_WIDTH = DOC_ELE.clientWidth > dprDesignWidth ? dprDesignWidth : DOC_ELE.clientWidth
DOC_ELE.style.fontSize = (IDEAL_WIDTH / (designWidth / split)).toFixed(4) + 'px'
return getPx2RemFunc(split)
}
/**
* 生成 px2rem 函数
* @param split
*/
export const getPx2RemFunc = (split: number): ((pixel: number) => string) => (pixel: number) => (pixel / split).toFixed(4) + 'rem'
设备环境识别
const UA: string = navigator.userAgent
const isInUA = (value: string) => (new RegExp(value, 'i')).test(UA)
export const isIOS = (): boolean => isInUA('(iPhone)|(iPad)|(iPod)')
export const isIPhone = (): boolean => isInUA('iPhone')
export const isIPad = (): boolean => isInUA('iPad')
export const isIPod = (): boolean => isInUA('iPod')
export const isAndroid = (): boolean => isInUA('Android')
export const isWeChat = (): boolean => isInUA('MicroMessenger')
类型识别
export const isString = (value: any) => typeof value === 'string'
export const isNumber = (value: any) => typeof value === 'number'
export const isBoolean = (value: any) => typeof value === 'boolean'
export const isNull = (value: any) => value === null
export const isUndefined = (value: any) => value === void 0
export const isObject = (value: any) => !isNull(value) && typeof value === 'object'
export const isJSONObject = (value: any) => {
if (isObject(value)) {
try {
JSON.stringify(value)
return true
} catch (error) {
return false
}
}
return false
}
正则
/**
* 网络 IPv4 地址
* @example 192.168.0.1
*/
export const IPV4_RULE = /^((25[0-5]|2[0-4]\d|1\d{2}|[1-9]\d|\d)\.){3}(25[0-5]|2[0-4]\d|1\d{2}|[1-9]\d|\d)$/;
/**
* 网络 IPv6 地址
* @example 2001:0db8:85a3:0000:0000:8a2e:0370:7334
*/
const IPV6_RULE = /^([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}$/;
/**
* 中国内地手机号码
* @example 18818188888
*/
export const CHINESE_PHONE_RULE = /^1[3-9]\d{9}$/;
/**
* 中国内地座机号码
* @example 028-999999999
*/
export const CHINESE_LANDLINE_RULE = /^0\d{2,3}-\d{7,8}$/;
/**
* 中国内地邮政编码
* @example 600000
*/
export const CHINESE_POSTAL_CODE_RULE = /^[1-9]\d{5}$/;
/**
* 中国居民身份证号码
* @example 511321200001011238
*/
export const CHINESE_ID_RULE = /^[1-9]\d{5}(18|19|20)\d{2}(0\d|1[0-2])([0-2]\d|3[0-1])\d{3}[\dX]$/;
/**
* 中国车牌号
* @example 川A88888
*/
export const CHINESE_LICENSE_PLATE_RULE = /^[\u4e00-\u9fa5][A-Z][A-Z_0-9]{5}$/;
/**
* 中国内地用户主流邮箱地址 qq.com 163.com 126.com 139.com foxmail.com gmail.com outlook.com icloud.com
* @example qq.com
*/
export const CHINESE_MAIN_MAIL_RULE = /^(qq|163|126|139|foxmail|gmail|outlook|icloud)\.com$/;
/**
* 迅雷链接正则
* @example thunder://afoia812na98f0a123123 thunderx://afoia812na98f0a123123
*/
export const THUNDERBOLT_RULE = /^thunderx?:\/\/[a-zA-Z\d]+=$/;
/**
* 软件版本号
* @example 1.0.0
*/
export const SOFTWARE_VERSION_RULE = /^\d+(\.\d+){0,2}$/;
/**
* 通用邮箱地址
* @example me@jiluo.cc
*/
export const MAIL_RULE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
/**
* HTML 注释
* @example <!-- header -->
*/
export const HTML_COMMENT_RULE = /<!--[\s\S]*?-->/;
// 图片文件格式(后缀)
export const IMAGE = /^(jpe?g|png|gif|bmp)$/i;
/**
* 视频文件格式(后缀)
* @example mp4
*/
export const VIDEO_RULE = /^(mp4|avi|mov|wmv|flv|mkv)$/i;
/**
* 音频文件格式(后缀)
* @example mp3
*/
export const AUDIO_RULE = /^(mp3|wav|ogg|flac)$/i;
/**
* 文件名
*/
export const FILE_NAME_RULE = /^[^\\/:*?"<>|]+$/;
/**
* GUID/UUID
* @example 01234567-89ab-cdef-0123-456789abcdef
*/
export const UUID_RULE = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
/**
* 简体中文汉字以及标点符号
* @example 你好
*/
export const SIMPLIFIED_CHINESE_RULE = /^[\u4e00-\u9fa5,。、;:?!]+$/;
/**
* 繁体中文汉字以及标点符号
* 美麗
*/
export const TRADITIONAL_CHINESE_RULE = /^[\u4e00-\u9fa5,。、;:?!「」『』【】《》()[]{}]+$/;
/**
* 小数
* @example 0.1 -0.1 1.0 -1.0 1.23 -1.23
*/
export const DECIMAL_RULE = /^-?\d+(\.\d+)?$/;
/**
* 整数
* @example 0 1 2 3 -1 -2 -3
*/
export const INTEGER_RULE = /^-?\d+$/;
/**
* QQ 号;5 至 11 位数字
* @example 153583876 305384014
*/
export const QQ_RULE = /^[1-9][0-9]{4,10}$/;
/**
* 微信号;6 至 20 位;字母、数字、减号、下划线组合;以字母开头
* @example jiluo-cc
*/
export const WE_CHAT_RULE = /^[a-zA-Z][-_a-zA-Z0-9]{5,19}$/;
/**
* CSS 颜色值(16 进制);支持 3 位或 6 位
* @example #fff #ffffff
*/
export const CSS_COLOR_RULE = /^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/;
/**
* URL 地址
* @example https://www.dongxi.dev
*/
export const URL_RULE = /^(https?|ftp):\/\/[^\s/$.?#].[^\s]*$/;
/**
* 域名(不包含非英文域名)
* @example dongxi.dev
*/
export const DOMAIN_RULE = /^((?!-)[A-Za-z0-9-]{1,63}(?<!-)\.)+[A-Za-z]{2,6}$/;
export const trim = (string: string) => string.replace(/^\s+|\s+$/gm, '')