UNPKG

petite-utils

Version:

A collection of small util function in JavaScript.

1 lines 33.4 kB
{"version":3,"file":"index.cjs","sources":["../src/type/type.ts","../src/utils/clone.ts","../src/utils/chunk.ts","../src/utils/copy.ts","../src/utils/debounce.ts","../src/type/identityCodeValid.ts","../src/type/isChinesePhone.ts","../src/type/isEqualArray.ts","../src/type/isHttpUrl.ts","../src/math/index.ts","../src/utils/normalizeMoney.ts","../src/utils/randomNum.ts","../src/utils/randomStr.ts","../src/utils/removeRepeat.ts","../src/utils/repeatRun.ts","../src/utils/throttle.ts","../src/utils/uuid.ts"],"sourcesContent":["/**\n * @group 类型检查\n * @param {any} value 需要检查类型的值\n * @returns {string} 返回类型的小写字符串\n */\nfunction type(value: any): string {\n const typeStr = Object.prototype.toString.call(value)\n return typeStr.slice(8, -1).toLowerCase()\n}\n\n/**\n * @group 类型检查\n * @param value\n * @returns\n */\nfunction isObject(value: any) {\n return typeof value === 'object' && value !== null\n}\n/**\n * @group 类型检查\n * @param value\n * @returns\n */\nfunction isPrimitive(value: any) {\n return (\n value === null ||\n typeof value === 'boolean' ||\n typeof value === 'number' ||\n typeof value === 'bigint' ||\n typeof value === 'string' ||\n typeof value === 'symbol' || // ES6 symbol\n typeof value === 'undefined'\n )\n}\n/**\n * @group 类型检查\n * @param e\n * @returns\n */\nfunction isError(e: any) {\n return type(e) === 'error' || e instanceof Error\n}\n\n/**\n * @group 类型检查\n * @param value\n * @returns\n */\nfunction isDate(d: any) {\n return type(d) === 'date'\n}\n\n/**\n * @group 类型检查\n * @param value\n * @returns\n */\nfunction isArray(value: any) {\n if (Array.isArray) {\n return Array.isArray(value)\n }\n return type(value) === 'array'\n}\n\n/**\n * @group 类型检查\n * @param value\n * @returns\n */\nfunction isBoolean(value: any) {\n return typeof value === 'boolean'\n}\n\n/**\n * @group 类型检查\n * @param value\n * @returns\n */\nfunction isNull(value: any) {\n return value === null\n}\n\n/**\n * @group 类型检查\n * @description 检查是否为 null 或者 undefined\n * @param value\n * @returns\n */\nfunction isNullish(value: any) {\n // https://stackoverflow.com/a/46716365/6524962\n return value == null\n}\n\n\n/**\n * @group 类型检查\n * @description 检查给定的值是否为数字 是 number 或 bigint,则返回 true,否则返回 false\n * @param value - 要检查的值,可以是任何类型\n * @return 如果是数字或 BigInt,则返回 true,否则返回 false\n */\nfunction isNumber(value: any) {\n // NaN, Infinity, -Infinity\n // NaN - NaN = NaN\n // Infinity - Infinity = NaN\n // -Infinity - -Infinity = NaN\n // Infinity * 0 = NaN\n // NaN * 0 = NaN\n // -Infinity * 0 = NaN\n if (typeof value === 'number') return value * 0 === 0\n if (typeof value === 'bigint') return true\n return false\n}\n/**\n * @group 类型检查\n * @description 检查给定的值是否为数值,包括数字、字符串数字 和 BigInt\n * 此外,如果给定值是一个非空字符串,它将尝试将其转换为数字并检查其有效性\n * @param value - 要检查的值,可以是任何类型\n * @return 如果是数字或可转换为有效数字的字符串,则返回 true,否则返回 false\n */\nfunction isNumerical(value: any) {\n if (typeof value === 'number') return value * 0 === 0\n if (typeof value === 'bigint') return true\n if (isString(value) && value.trim() !== '') {\n return Number.isFinite ? Number.isFinite(+value) : isFinite(+value)\n }\n return false\n}\n\n/**\n * @group 类型检查\n * @param value\n * @returns\n */\nfunction isString(value: any) {\n return typeof value === 'string'\n}\n\n/**\n * @group 类型检查\n * @param value\n * @returns\n */\nfunction isEmptyStr(value: any) {\n return value === ''\n}\n\n/**\n * @group 类型检查\n * @param value\n * @returns\n */\nfunction hasStr(value: any) {\n return isString(value) && value !== ''\n}\n\n/**\n * @group 类型检查\n * @param value\n * @returns\n */\nfunction isSymbol(value: any) {\n return typeof value === 'symbol'\n}\n\n/**\n * @group 类型检查\n * @param value\n * @returns\n */\nfunction isUndefined(value: any) {\n return value === void 0\n}\n\n/**\n * @group 类型检查\n * @param value\n * @returns\n */\nfunction isRegExp(value: any) {\n return type(value) === 'regexp'\n}\n\n/**\n * @group 类型检查\n * @param value\n * @returns\n */\nfunction isFalsy(value: any) {\n return !value // 0 '' NaN null undefined\n}\n\n/**\n * @group 类型检查\n * @param value\n * @returns\n */\nfunction isFalsyNon0(value: any) {\n return !value && value !== 0\n}\n/**\n * @group 类型检查\n * @param value\n * @returns\n */\nfunction isTruthy(value: any) {\n return !!value\n}\n\n/**\n * @group 类型检查\n * @param value\n * @returns\n */\nfunction isFunction(value: any) {\n if (isNullish(value)) return false\n if (value) {\n return (\n type(value) === 'function' ||\n 'function' === typeof value ||\n value instanceof Function\n )\n }\n return false\n}\n\n/**\n * @group 类型检查\n * @param value\n * @returns\n */\nfunction isEmpty(value: any) {\n // ''\n if (isEmptyStr(value)) return true\n // []\n if (Array.isArray(value)) return value.length === 0\n // null undefined\n if (isNullish(value)) return true\n if (isObject(value)) {\n for (var prop in value) {\n if (value.hasOwnProperty(prop)) return false\n }\n return true\n }\n return false\n}\n\nexport {\n type,\n isObject,\n isArray,\n isBoolean,\n isDate,\n isError,\n isNull,\n isUndefined,\n isNullish,\n isNumber,\n isNumerical,\n isString,\n isSymbol,\n isRegExp,\n isPrimitive,\n isFalsy,\n isFalsyNon0,\n isTruthy,\n isFunction,\n isEmptyStr,\n isEmpty,\n}\n","/*\n * @Author : ZhouQiJun\n * @Date : 2023-04-29 20:19:57\n * @LastEditors : ZhouQiJun\n * @LastEditTime: 2025-03-22 22:31:32\n * @Description : 深复制\n */\nimport { type } from '../type/type'\n\n// import from from 'core-js/features/array/from'\n/**\n * @group 工具函数\n * @param {any} source\n * @returns\n */\nconst clone = <T>(source: T) => {\n const t = type(source)\n if (!['object', 'array'].includes(t)) return source\n let target\n if (t === 'array') {\n target = []\n let i = 0\n const len = (source as Array<any>).length\n while (i < len) {\n target[i] = clone(source[i])\n i++\n }\n } else if (t === 'object') {\n target = {}\n for (const key in source) {\n if (Object.prototype.hasOwnProperty.call(source, key)) {\n // @ts-ignore\n target[key] = clone(source[key])\n }\n }\n }\n return target\n}\n\nexport { clone }\n","/*\n * @Author : ZhouQiJun\n * @Date : 2024-08-18 00:32:26\n * @LastEditors : ZhouQiJun\n * @LastEditTime: 2025-03-22 22:29:50\n * @Description : 数组分块\n */\nimport { isNumber } from '../type/type'\n\n/**\n * @group 工具函数\n * @param array 数组\n * @param size 分块大小\n * @returns 返回分块后的数组\n */\nexport function chunk<T>(array: T[], size: number): T[][] {\n if (!Array.isArray(array)) {\n throw new Error('第一个参数必须是一个一维数组')\n }\n if (!isNumber(size)) {\n throw new Error('第二个参数必须是一个数字')\n }\n const result: T[][] = []\n for (let i = 0; i < array.length; i += size) {\n result.push(array.slice(i, i + size))\n }\n return result\n}\n","/* eslint-disable @typescript-eslint/no-unused-vars */\n/*\n * @Author : ZhouQiJun\n * @Date : 2024-05-28 10:20:59\n * @LastEditors : ZhouQiJun\n * @LastEditTime: 2025-03-22 21:54:35\n * @Description : 复制文本\n */\n/**\n * @group 工具函数\n * @description copy text\n * @param text some text you want to copy\n * @returns\n */\nexport function copy(text: string): Promise<boolean> {\n if (navigator.clipboard && navigator.permissions) {\n return navigator.clipboard\n .writeText(text)\n .then(() => {\n return Promise.resolve(true)\n })\n .catch(() => {\n return Promise.resolve(false)\n })\n } else {\n return new Promise(resolve => {\n const textArea = document.createElement('textArea')\n // @ts-ignore\n textArea.value = text\n textArea.style.width = '0px'\n textArea.style.position = 'fixed'\n textArea.style.left = '-999px'\n textArea.style.top = '10px'\n textArea.setAttribute('readonly', 'readonly')\n document.body.appendChild(textArea)\n // @ts-ignore\n textArea.select()\n document.execCommand('copy')\n document.body.removeChild(textArea)\n resolve(true)\n })\n }\n}\n","/*\n * @Author : ZhouQiJun\n * @Date : 2024-05-28 03:00:17\n * @LastEditors : ZhouQiJun\n * @LastEditTime: 2025-03-23 21:13:48\n * @Description : 防抖\n */\n/**\n * @group 工具函数\n * @param fn function need to debounce\n * @param wait debounce time default 200ms\n * @param immediate if true, execute immediately when trigger, default false\n * @returns\n */\nexport function debounce(fn, wait = 200, immediate = false) {\n let timer\n return (...rest) => {\n immediate && !timer && fn(...rest)\n if (timer) {\n clearTimeout(timer)\n }\n timer = setTimeout(() => {\n fn(...rest)\n }, wait)\n }\n}\n","import { isEmptyStr, isNullish, isNumber, isString } from './type'\n\n/**\n * @group 类型检查\n * @description 身份证号合法性验证,支持15位和18位身份证号,支持地址编码、出生日期、校验位验证\n * @param code\n * @returns {[boolean,string]} 该函数返回一个数组 [true,''] 或 [false,\"身份证号格式错误\"]\n * ============================================================================================\n * 根据〖中华人民共和国国家标准 GB 11643-1999〗中有关公民身份号码的规定,公民身份号码是特征组合码,由十七位数字本体码和一位数字校验码组成。\n * 排列顺序从左至右依次为:六位数字地址码,八位数字出生日期码,三位数字顺序码和一位数字校验码。\n * 地址码表示编码对象常住户口所在县(市、旗、区)的行政区划代码。\n * 出生日期码表示编码对象出生的年、月、日,其中年份用四位数字表示,年、月、日之间不用分隔符。\n * 顺序码表示同一地址码所标识的区域范围内,对同年、月、日出生的人员编定的顺序号。顺序码的奇数分给男性,偶数分给女性。\n * 校验码是根据前面十七位数字码,按照ISO 7064:1983.MOD 11-2校验码计算出来的检验码。\n * 出生日期计算方法。\n * 15位的身份证编码首先把出生年扩展为4位,简单的就是增加一个19或18,这样就包含了所有1800-1999年出生的人;\n * 2000年后出生的肯定都是18位的了没有这个烦恼,至于1800年前出生的,那啥那时应该还没身份证号这个东东,⊙﹏⊙b汗...\n * 下面是正则表达式:\n * 出生日期1800-2099 (18|19|20)?\\d{2}(0[1-9]|1[12])(0[1-9]|[12]\\d|3[01])\n * 身份证正则表达式 /^\\d{6}(18|19|20)?\\d{2}(0[1-9]|1[12])(0[1-9]|[12]\\d|3[01])\\d{3}(\\d|X)$/i\n * 15位校验规则 6位地址编码+6位出生日期+3位顺序号\n * 18位校验规则 6位地址编码+8位出生日期+3位顺序号+1位校验位\n * 校验位规则 公式:∑(ai×Wi)(mod 11)……………………………………(1)\n * 公式(1)中:\n * i----表示号码字符从由至左包括校验码在内的位置序号;\n * ai----表示第i位置上的号码字符值;\n * Wi----示第i位置上的加权因子,其数值依据公式Wi=2^(n-1)(mod 11)计算得出。\n * i 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1\n * Wi 7 9 10 5 8 4 2 1 6 3 7 9 10 5 8 4 2 1\n *\n */\nfunction identityCodeValid(code: string | number): [boolean, string] {\n if (isNullish(code) || isEmptyStr(code)) return [false, '身份证号不能为空']\n if (!isNumber(code) && !isString(code))\n return [false, '身份证号必须是数字或者字符串']\n code = code.toString()\n const city: any = {\n 11: '北京',\n 12: '天津',\n 13: '河北',\n 14: '山西',\n 15: '内蒙古',\n 21: '辽宁',\n 22: '吉林',\n 23: '黑龙江 ',\n 31: '上海',\n 32: '江苏',\n 33: '浙江',\n 34: '安徽',\n 35: '福建',\n 36: '江西',\n 37: '山东',\n 41: '河南',\n 42: '湖北 ',\n 43: '湖南',\n 44: '广东',\n 45: '广西',\n 46: '海南',\n 50: '重庆',\n 51: '四川',\n 52: '贵州',\n 53: '云南',\n 54: '西藏 ',\n 61: '陕西',\n 62: '甘肃',\n 63: '青海',\n 64: '宁夏',\n 65: '新疆',\n 71: '台湾',\n 81: '香港',\n 82: '澳门',\n 91: '国外 ',\n }\n let tip = ''\n let pass = true\n\n if (code.length !== 15 && code.length !== 18) {\n return [false, '身份证号长度错误, 必须是15位或者18位']\n }\n\n const codeRegex =\n /^\\d{6}(18|19|20)?\\d{2}(0[1-9]|1[012])(0[1-9]|[12]\\d|3[01])\\d{3}(\\d|[xX])$/i\n const isFormatCorrect = codeRegex.test(code)\n\n if (!isFormatCorrect) {\n return [false, '身份证号格式错误']\n }\n\n if (!city[code.slice(0, 2)]) {\n return [false, '地址编码错误']\n }\n\n //18位身份证需要验证最后一位校验位\n if (code.length == 18) {\n const arr = code.split('')\n //∑(ai×Wi)(mod 11)\n //加权因子\n const factor = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2]\n //校验位\n const parity = [1, 0, 'X', 9, 8, 7, 6, 5, 4, 3, 2]\n let sum = 0\n let ai = 0\n let wi = 0\n for (let i = 0; i < 17; i++) {\n ai = +arr[i]\n wi = factor[i]\n sum += ai * wi\n }\n const last = parity[sum % 11]\n if (last != arr[17]) {\n tip = '校验位错误,结尾是字母请注意大小写'\n pass = false\n }\n }\n\n return [pass, tip]\n}\n\nexport { identityCodeValid }\n","/*\n * @Author : ZhouQiJun\n * @Date : 2024-05-01 18:32:48\n * @LastEditors : ZhouQiJun\n * @LastEditTime: 2025-03-22 22:42:35\n * @Description : 检查是否为中国大陆号码\n */\n\n/**\n * @group 类型检查\n * @param number 待检测的手机号码\n * @returns\n */\nexport function isChinesePhone(number: string): boolean {\n // const phoneReg =\n // /^(((13[0-9]{1})|(14[0-9]{1})|(15[0-9]{1})|(16[0-9]{1})|(17[0-9]{1})|(18[0-9]{1}))+\\d{8})$/\n const phoneReg = /^1[3-9]\\d{9}$/\n return phoneReg.test(number)\n}\n","/*\n * @Author : ZhouQiJun\n * @Date : 2024-05-01 19:22:50\n * @LastEditors : ZhouQiJun\n * @LastEditTime: 2025-03-22 22:44:55\n * @Description : 数组元素是否相同\n */\n/**\n * @group 类型检查\n * @param arr1 第一个数组\n * @param arr2 第二个数组\n * @returns\n */\nexport function isEqualArray(arr1: any[], arr2: any[]): boolean {\n if (arr1.length !== arr2.length) {\n return false\n }\n for (let i = 0; i < arr1.length; i++) {\n if (arr1[i] !== arr2[i]) {\n return false\n }\n }\n return true\n}\n","/*\n * @Author : ZhouQiJun\n * @Date : 2024-05-02 00:01:01\n * @LastEditors : ZhouQiJun\n * @LastEditTime: 2025-03-22 22:43:44\n * @Description : 检查字符是否为 http 网址\n */\n/**\n * @group 类型检查\n * @param string 检查字符串是否为 http 地址\n * @returns\n */\nexport function isHttpUrl(string: string): boolean {\n let url\n\n try {\n url = new URL(string)\n } catch (_) {\n return false\n }\n\n return url.protocol === 'http:' || url.protocol === 'https:'\n}\n","/*\n * @Author : ZhouQiJun\n * @Date : 2024-11-28 11:34:51\n * @LastEditors : ZhouQiJun\n * @LastEditTime: 2024-11-28 12:59:40\n * @Description : math 相关函数\n */\n/**\n * m mod n,\n * it is different from % operator in javascript\n * % operator is remainder operator, m % n is same to m - n * Math.trunc(m / n).\n * Modulo operator is m mod n is same to m - n * Math.floor(m / n)\n * {@link https://www.designcise.com/web/tutorial/what-is-the-difference-between-the-javascript-remainder-operator-and-the-modulo-operator}\n * @example\n * modulo(7, 3) // 1\n * modulo(-7, -3) // -1\n * modulo(7, -3) // -2\n * modulo(-7, 3) // 2\n * @param m\n * @param n\n * @returns\n */\nfunction modulo(m: number, n: number): number {\n // same to\n // m - n * Math.floor(m / n)\n return ((m % n) + n) % n\n}\n\n/**\n * m remainder n 取余数\n * @example\n * remainder(7, 3) // 1\n * remainder(-7, 3) // -1\n * remainder(7, -3) // 1\n * remainder(-7, -3) // -1\n * @param dividend\n * @param divisor\n * @returns remainder\n * {@link https://stackoverflow.com/questions/38702724/math-floor-vs-math-trunc-javascript}\n */\nfunction remainder(dividend: number, divisor: number): number {\n const quotient = Math.trunc(dividend / divisor)\n return dividend - divisor * quotient\n}\n\nexport { modulo, remainder }\n","/*\n * @Author : ZhouQiJun\n * @Date : 2024-07-16 23:03:23\n * @LastEditors : ZhouQiJun\n * @LastEditTime: 2025-03-22 22:09:54\n * @Description : 格式化金额\n */\nimport { isNumber, isString } from '../type/type'\n/**\n * @group 工具函数\n * @param money 金钱数额\n * @returns\n */\nexport function normalizeMoney(money: number | string): string {\n if ((!isNumber(money) && !isString(money)) || money === '') {\n return '0'\n }\n\n money = money.toString()\n if (/[^0-9\\.]/.test(money)) return '0'\n\n if (+money === 0) return '0.00'\n\n money = money.replace(/^(\\d*)$/, '$1.')\n money = (money + '00').replace(/(\\d*\\.\\d\\d)\\d*/, '$1')\n money = money.replace('.', ',')\n\n const re = /(\\d)(\\d{3},)/\n while (re.test(money)) {\n money = money.replace(re, '$1,$2')\n }\n money = money.replace(/,(\\d\\d)$/, '.$1')\n // 不带小数位(默认是有小数位)\n // if (precision == 0) {\n // var a = money.split('.')\n // if (a[1] == '00') {\n // money = a[0]\n // }\n // }\n return money\n}\n","/*\n * @Author : ZhouQiJun\n * @Date : 2025-03-06 16:02:51\n * @LastEditors : ZhouQiJun\n * @LastEditTime: 2025-03-22 21:59:23\n * @Description : randomNum 函数 - 生成随机数\n */\n\n/**\n * @group 工具函数\n * @param [length=6] 长度\n * @returns\n */\nexport function randomNum(length: number = 6) {\n const arr: number[] = []\n let n = ''\n const str = '1234566789'\n for (var i = 0; i < length; i++) {\n if (i === 0) {\n arr[i] = +str[Math.ceil(Math.random() * 9)]\n } else {\n arr[i] = Number.parseInt(Math.random() * 10 + '')\n }\n n += String(arr[i])\n }\n return Number.parseInt(n)\n}\n","/*\n * @Author : ZhouQiJun\n * @Date : 2024-07-16 23:03:23\n * @LastEditors : ZhouQiJun\n * @LastEditTime: 2025-03-22 22:00:48\n * @Description : 随机字符串\n */\nimport { isBoolean, isNumber } from '../type/type'\n\n/**\n * @group 工具函数\n * @param [min=7] 最小长度\n * @param [max=36] 最大长度\n * @param [startLetter=true] 是否需要字母开头\n * @returns\n */\nexport function randomStr(\n min: number | string = 7,\n max: number | string = 36,\n startLetter = true,\n) {\n if (!isNumber(+min) || !isNumber(+max) || !isBoolean(startLetter)) {\n return 'min, max should be number, startLetter should be boolean'\n }\n if (+min < 0 || +max < 0) {\n return 'min, max should be positive number'\n }\n if (min > max) {\n return 'min should be less than max'\n }\n min = +min\n max = +max\n const number = '0123456789'\n const letter = 'abcdefghijklmnopqrstuvwxyz'\n const LETTER = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'\n const all = number + letter + LETTER\n const allLetters = letter + LETTER\n let str = ''\n const length = Math.floor(Math.random() * (max - min)) + min\n for (let i = 0; i < length; i++) {\n const index = Math.floor(Math.random() * all.length)\n str += all[index]\n }\n if (startLetter && /^[0-9]/.test(str)) {\n const index = Math.floor(Math.random() * allLetters.length)\n str = allLetters[index] + str.slice(1)\n }\n if (!startLetter && /^[a-zA-Z]/.test(str)) {\n const index = Math.floor(Math.random() * number.length)\n str = number[index] + str.slice(1)\n }\n return str\n}\n","/*\n * @Author : ZhouQiJun\n * @Date : 2024-05-01 20:10:15\n * @LastEditors : ZhouQiJun\n * @LastEditTime: 2025-03-22 22:02:43\n * @Description : 数组去重\n */\n/**\n * @group 工具函数\n * @param arr 数组\n * @returns\n */\nexport function removeRepeat(arr: any[]) {\n if (!Array.isArray(arr)) {\n return []\n }\n return Array.from(new Set(arr))\n}\n","/*\n * @Author : ZhouQiJun\n * @Date : 2024-10-28 01:04:56\n * @LastEditors : ZhouQiJun\n * @LastEditTime: 2025-03-22 21:40:58\n * @Description : repeatRun 函数 - 重复执行函数\n * 解决 setInterval 时间不精确的问题\n */\nimport { isFunction, isNumber } from '../type/type'\n\n/**\n * @description: stop run repeat-function\n */\nexport type StopRepeat = () => void\n\nexport type RepeatFn = (\n repeatTimes: number,\n stop: StopRepeat,\n params?: unknown,\n) => void\n\nexport interface RepeatOptions {\n interval?: number\n immediate?: boolean\n}\n\n/**\n * ## repeat call function\n * @group 工具函数\n * @param fn will be called repeatly\n * @param param1.interval interval time default 1000\n * @param param1.immediate call fn immediately default false\n * @param params params pass to fn\n * @returns stopFunction to stop repeat\n * @example\n * ### pass a arrow function to repeat\n * ```ts\n * repeatRun((repeatTimes, stop) => {\n * console.log(repeatTimes)\n * if (repeatTimes === 5) {\n * stop() // stop function from fn params\n * }\n * })\n * ```\n * @example\n * ### pass a function definition to repeat and stop it by repeat return value\n * ```ts\n * let stop = repeatRun(sayHi, { interval: 1000 })\n * function sayHi(repeatTimes) {\n * console.log(repeatTimes)\n * if (repeatTimes === 5) {\n * stop() // stop from repeatRun return value\n * }\n * }\n * ```\n */\nexport function repeatRun(\n fn: RepeatFn,\n { interval = 1000, immediate = false }: RepeatOptions = {},\n params?: unknown,\n): StopRepeat {\n if (!isFunction(fn)) {\n throw new Error('first argument must be a function')\n }\n\n if (!isNumber(interval) || interval <= 0) {\n throw new Error('interval must be a positive number')\n }\n const stop: StopRepeat = () => {\n hasStopped = true\n clearTimeout(timer)\n clearTimeout(timer2)\n }\n let hasStopped = false\n let timer2\n let repeatTimes = 0\n if (immediate) {\n ++repeatTimes\n fn(repeatTimes, stop, params)\n }\n let timer = setTimeout(function repeat() {\n if (hasStopped) {\n return\n }\n ++repeatTimes\n fn(repeatTimes, stop, params)\n timer2 = setTimeout(repeat, interval)\n }, interval)\n return stop\n}\n\n// 函数定义和函数表达式的区别\n// 函数定义会被提升,var 的函数表达式不会被提升,let const 的函数表达式不会被提升\n// let stop = repeat(hello)\n// // this is ok, because function definition is hoisted\n// function hello(_, times) {\n// console.log('hello', times)\n// if (times === 10) {\n// stop()\n// }\n// }\n// // this is not ok, because var initialization is hoisted ❌\n// var hello = function (_, time) {\n// console.log('hello', times)\n// if (times === 10) {\n// stop()\n// }\n// }\n// // this is not ok, because let and const variable are not hoisted ❌\n// let hello = (_, time) => {\n// console.log('hello', times)\n// if (times === 10) {\n// stop()\n// }\n// }\n","/*\n * @Author : ZhouQiJun\n * @Date : 2024-05-28 03:04:01\n * @LastEditors : ZhouQiJun\n * @LastEditTime: 2025-03-23 21:09:29\n * @Description : 节流\n */\n/**\n * @group 工具函数\n * @param fn 待节流的函数\n * @param interval 间隔时间,毫秒,默认 200\n * @param immediate 是否立即执行,默认 false\n * @returns\n */\nexport function throttle(fn, interval = 200, immediate = false) {\n let last = Date.now() // 上次执行的时间戳\n let timer = null // 定时器\n let hasCalled = false // 是否是第一次调用\n\n return (...rest) => {\n const now = Date.now()\n\n if (immediate && !hasCalled) {\n // 如果是第一次调用且需要立即执行\n fn(...rest)\n last = now\n hasCalled = true\n }\n\n clearTimeout(timer) // 清除之前的定时器\n\n if (now - last >= interval) {\n // 如果距离上次执行的时间超过 interval,则立即执行\n fn(...rest)\n last = now\n } else {\n // 否则,设置定时器,在剩余时间后执行\n timer = setTimeout(\n () => {\n fn(...rest)\n last = Date.now()\n },\n // 保证时间间隔为 interval 内\n interval - (now - last),\n )\n }\n }\n}\n","/*\n * @Author : ZhouQiJun\n * @Date : 2024-05-01 18:25:22\n * @LastEditors : ZhouQiJun\n * @LastEditTime: 2025-03-22 22:05:37\n * @Description : 生成 uuid\n */\n/**\n * @group 工具函数\n * @returns\n */\nexport function uuid() {\n let d = new Date().getTime()\n const uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(\n /[xy]/g,\n function (c) {\n var r = (d + Math.random() * 16) % 16 | 0\n d = Math.floor(d / 16)\n const str = c === 'x' ? r : (r & 0x7) | 0x8\n return str.toString(16)\n },\n )\n return uuid\n}\n"],"names":["type","value","Object","prototype","toString","call","slice","toLowerCase","isObject","isBoolean","isNullish","isNumber","isString","isEmptyStr","isFunction","Function","clone","source","target","t","includes","i","len","length","key","hasOwnProperty","array","size","Array","isArray","Error","result","push","text","navigator","clipboard","permissions","writeText","then","Promise","resolve","textArea","document","createElement","style","width","position","left","top","setAttribute","body","appendChild","select","execCommand","removeChild","fn","wait","immediate","timer","rest","arguments","apply","clearTimeout","setTimeout","code","tip","pass","test","arr","split","factor","sum","number","d","prop","arr1","arr2","e","string","url","URL","_","protocol","trim","Number","isFinite","m","n","money","replace","re","Math","ceil","random","parseInt","String","min","max","startLetter","letter","LETTER","all","allLetters","str","floor","dividend","divisor","trunc","from","Set","_temp","params","_ref","_ref$interval","interval","_ref$immediate","timer2","stop","hasStopped","repeatTimes","repeat","last","Date","now","hasCalled","getTime","c","r"],"mappings":"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"}