JS 工具函数(utils)大全
JS 工具函数(utils)是项目中复用率极高的通用函数集合,覆盖类型判断、数据处理、时间格式化、浏览器操作、数组/对象处理等场景,能大幅提升开发效率。以下是高频实用的工具函数,按场景分类整理,可直接集成到项目中。
一、基础类型判断
解决typeof不准确、复杂类型识别问题,返回精准的类型字符串(小写)。
/** * 精准判断数据类型 * @param {*} value 要判断的值 * @returns {string} 类型字符串(如:string/number/array/object/null/undefined等) */exportconstgetType=(value)=>{returnObject.prototype.toString.call(value).slice(8,-1).toLowerCase();};/** * 判断是否为字符串 * @param {*} value * @returns {boolean} */exportconstisString=(value)=>getType(value)==='string';/** * 判断是否为数字(排除 NaN) * @param {*} value * @returns {boolean} */exportconstisNumber=(value)=>getType(value)==='number'&&!isNaN(value);/** * 判断是否为数组 * @param {*} value * @returns {boolean} */exportconstisArray=(value)=>Array.isArray(value)||getType(value)==='array';/** * 判断是否为纯对象(排除 null/数组/函数) * @param {*} value * @returns {boolean} */exportconstisPlainObject=(value)=>getType(value)==='object';/** * 判断是否为函数 * @param {*} value * @returns {boolean} */exportconstisFunction=(value)=>getType(value)==='function';/** * 判断是否为 null/undefined/空字符串/空数组/空对象 * @param {*} value * @returns {boolean} */exportconstisEmpty=(value)=>{if(value===null||value===undefined)returntrue;if(isString(value))returnvalue.trim()==='';if(isArray(value))returnvalue.length===0;if(isPlainObject(value))returnObject.keys(value).length===0;returnfalse;};二、数组工具函数
处理数组去重、扁平化、查找、分组等高频需求。
/** * 数组去重(支持基本类型,复杂类型需自定义 key) * @param {Array} arr 源数组 * @param {string} [key] 复杂对象去重的键(如 'id') * @returns {Array} 去重后的数组 */exportconstuniqueArray=(arr,key)=>{if(!isArray(arr))return[];if(key){constmap=newMap();returnarr.filter(item=>!map.has(item[key])&&map.set(item[key],true));}return[...newSet(arr)];};/** * 数组扁平化(支持指定深度) * @param {Array} arr 源数组 * @param {number} [depth=Infinity] 扁平化深度 * @returns {Array} 扁平化后的数组 */exportconstflattenArray=(arr,depth=Infinity)=>{if(!isArray(arr))return[];returnarr.flat(depth);// 兼容低版本 JS:// return arr.reduce((acc, val) => acc.concat(Array.isArray(val) && depth > 1 ? flattenArray(val, depth - 1) : val), []);};/** * 数组分组(按指定 key/函数分组) * @param {Array} arr 源数组 * @param {string|Function} key 分组键/分组函数 * @returns {Object} 分组结果 { key: [items] } */exportconstgroupArray=(arr,key)=>{if(!isArray(arr))return{};returnarr.reduce((groups,item)=>{constgroupKey=isFunction(key)?key(item):item[key];(groups[groupKey]||(groups[groupKey]=[])).push(item);returngroups;},{});};/** * 查找数组中符合条件的第一个元素的索引(兼容找不到的情况) * @param {Array} arr 源数组 * @param {Function} predicate 条件函数 * @returns {number} 索引(找不到返回 -1) */exportconstfindIndex=(arr,predicate)=>{if(!isArray(arr)||!isFunction(predicate))return-1;returnarr.findIndex(predicate);};三、对象工具函数
处理对象深拷贝、合并、取值、判空等。
/** * 深拷贝(支持基本类型、数组、对象,排除循环引用) * @param {*} obj 要拷贝的对象/值 * @returns {*} 拷贝后的结果 */exportconstdeepClone=(obj)=>{if(obj===null||typeofobj!=='object')returnobj;if(objinstanceofDate)returnnewDate(obj);if(objinstanceofRegExp)returnnewRegExp(obj);// 处理数组/对象constcloneObj=Array.isArray(obj)?[]:{};for(constkeyinobj){cloneObj[key]=deepClone(obj[key]);}returncloneObj;};/** * 合并多个对象(深合并,后面的对象覆盖前面的) * @param {...Object} objs 要合并的对象 * @returns {Object} 合并后的对象 */exportconstmergeObjects=(...objs)=>{constresult={};objs.forEach(obj=>{if(isPlainObject(obj)){for(constkeyinobj){result[key]=isPlainObject(obj[key])?mergeObjects(result[key]||{},obj[key]):obj[key];}}});returnresult;};/** * 安全获取对象嵌套属性(避免 Cannot read property 'xxx' of undefined) * @param {Object} obj 源对象 * @param {string|Array} path 属性路径(如 'a.b.c' 或 ['a', 'b', 'c']) * @param {*} defaultValue 默认值 * @returns {*} 属性值/默认值 */exportconstgetNestedValue=(obj,path,defaultValue=undefined)=>{if(!isPlainObject(obj)||isEmpty(path))returndefaultValue;constpathArr=isString(path)?path.split('.').filter(Boolean):path;returnpathArr.reduce((acc,key)=>(acc&&acc[key]!==undefined?acc[key]:defaultValue),obj);};/** * 删除对象中的空值属性(null/undefined/空字符串/空数组/空对象) * @param {Object} obj 源对象 * @returns {Object} 清理后的对象 */exportconstremoveEmptyProps=(obj)=>{if(!isPlainObject(obj))returnobj;constresult={};for(constkeyinobj){constvalue=obj[key];if(!isEmpty(value)){result[key]=isPlainObject(value)?removeEmptyProps(value):value;}}returnresult;};四、时间/日期工具函数
格式化时间、计算时间差、解析时间戳等。
/** * 格式化时间(默认:YYYY-MM-DD HH:mm:ss) * @param {Date|number|string} date 时间(Date对象/时间戳/字符串) * @param {string} format 格式(YYYY=年, MM=月, DD=日, HH=时, mm=分, ss=秒) * @returns {string} 格式化后的时间 */exportconstformatDate=(date,format='YYYY-MM-DD HH:mm:ss')=>{lettargetDate;if(isNumber(date))targetDate=newDate(date);// 时间戳elseif(isString(date))targetDate=newDate(date);// 时间字符串elseif(dateinstanceofDate)targetDate=date;elsereturn'';// 无效时间返回空constyear=targetDate.getFullYear();constmonth=String(targetDate.getMonth()+1).padStart(2,'0');constday=String(targetDate.getDate()).padStart(2,'0');consthour=String(targetDate.getHours()).padStart(2,'0');constminute=String(targetDate.getMinutes()).padStart(2,'0');constsecond=String(targetDate.getSeconds()).padStart(2,'0');returnformat.replace('YYYY',year).replace('MM',month).replace('DD',day).replace('HH',hour).replace('mm',minute).replace('ss',second);};/** * 计算两个时间的差值(返回天/时/分/秒) * @param {Date|number} start 开始时间 * @param {Date|number} end 结束时间 * @returns {Object} { days, hours, minutes, seconds } */exportconstgetTimeDiff=(start,end)=>{conststartMs=startinstanceofDate?start.getTime():Number(start);constendMs=endinstanceofDate?end.getTime():Number(end);constdiffMs=Math.abs(endMs-startMs);// 毫秒差(取绝对值)constseconds=Math.floor(diffMs/1000)%60;constminutes=Math.floor(diffMs/(1000*60))%60;consthours=Math.floor(diffMs/(1000*60*60))%24;constdays=Math.floor(diffMs/(1000*60*60*24));return{days,hours,minutes,seconds};};/** * 获取当前时间戳(秒/毫秒) * @param {boolean} [isSecond=false] 是否返回秒级时间戳 * @returns {number} 时间戳 */exportconstgetTimestamp=(isSecond=false)=>{constnow=Date.now();returnisSecond?Math.floor(now/1000):now;};/** * 判断是否为今天 * @param {Date|number|string} date 要判断的时间 * @returns {boolean} */exportconstisToday=(date)=>{consttargetDate=newDate(date);consttoday=newDate();return(targetDate.getFullYear()===today.getFullYear()&&targetDate.getMonth()===today.getMonth()&&targetDate.getDate()===today.getDate());};五、浏览器/URL 工具函数
处理 URL 参数、本地存储、浏览器特性检测等。
/** * 获取 URL 中的查询参数 * @param {string} [url=window.location.href] URL 地址 * @returns {Object} 查询参数对象 */exportconstgetUrlParams=(url=window.location.href)=>{constparams={};constsearch=newURL(url).search.slice(1);// 去掉 ?if(!search)returnparams;search.split('&').forEach(item=>{const[key,value]=item.split('=');if(key)params[decodeURIComponent(key)]=decodeURIComponent(value||'');});returnparams;};/** * 设置 URL 查询参数(返回新 URL) * @param {string} [url=window.location.href] 原 URL * @param {Object} params 要设置的参数 * @returns {string} 新 URL */exportconstsetUrlParams=(url=window.location.href,params)=>{if(!isPlainObject(params)||isEmpty(params))returnurl;consturlObj=newURL(url);Object.entries(params).forEach(([key,value])=>{if(value===undefined||value===null){urlObj.searchParams.delete(key);}else{urlObj.searchParams.set(key,value);}});returnurlObj.href;};/** * 本地存储封装(localStorage,支持过期时间) * @returns {Object} { set, get, remove, clear } */exportconststorage=(()=>{/** * 设置本地存储 * @param {string} key 键 * @param {*} value 值(支持任意类型) * @param {number} [expire] 过期时间(秒) */constset=(key,value,expire)=>{try{constdata={value,expire:expire?getTimestamp(true)+expire:null};localStorage.setItem(key,JSON.stringify(data));}catch(e){console.error('localStorage 设置失败:',e);}};/** * 获取本地存储 * @param {string} key 键 * @returns {*} 值(过期返回 null) */constget=(key)=>{try{conststr=localStorage.getItem(key);if(!str)returnnull;constdata=JSON.parse(str);// 检查过期if(data.expire&&data.expire<getTimestamp(true)){remove(key);returnnull;}returndata.value;}catch(e){console.error('localStorage 获取失败:',e);returnnull;}};/** * 删除本地存储 * @param {string} key 键 */constremove=(key)=>{try{localStorage.removeItem(key);}catch(e){console.error('localStorage 删除失败:',e);}};/** * 清空本地存储 */constclear=()=>{try{localStorage.clear();}catch(e){console.error('localStorage 清空失败:',e);}};return{set,get,remove,clear};})();/** * 防抖函数(高频操作触发后延迟执行,避免多次执行) * @param {Function} fn 目标函数 * @param {number} delay 延迟时间(毫秒) * @param {boolean} [immediate=false] 是否立即执行 * @returns {Function} 防抖后的函数 */exportconstdebounce=(fn,delay,immediate=false)=>{lettimer=null;returnfunction(...args){constcontext=this;if(timer)clearTimeout(timer);if(immediate&&!timer){fn.apply(context,args);}timer=setTimeout(()=>{fn.apply(context,args);timer=null;},delay);};};/** * 节流函数(高频操作在指定时间内只执行一次) * @param {Function} fn 目标函数 * @param {number} interval 间隔时间(毫秒) * @returns {Function} 节流后的函数 */exportconstthrottle=(fn,interval)=>{letlastTime=0;returnfunction(...args){constcontext=this;constnow=Date.now();if(now-lastTime>=interval){fn.apply(context,args);lastTime=now;}};};六、字符串工具函数
处理字符串脱敏、格式化、转义等。
/** * 字符串脱敏(如手机号、身份证、姓名) * @param {string} str 源字符串 * @param {number} [start=3] 保留前几位 * @param {number} [end=4] 保留后几位 * @param {string} [mask='*'] 掩码字符 * @returns {string} 脱敏后的字符串 */exportconstmaskString=(str,start=3,end=4,mask='*')=>{if(!isString(str)||str.length<=start+end)returnstr;constprefix=str.slice(0,start);constsuffix=str.slice(-end);constmaskLen=str.length-start-end;returnprefix+mask.repeat(maskLen)+suffix;};/** * 千分位格式化数字(如 1234567 → 1,234,567) * @param {number|string} num 数字/数字字符串 * @returns {string} 格式化后的字符串 */exportconstformatThousand=(num)=>{if(!isNumber(Number(num)))return'0';returnnum.toString().replace(/\B(?=(\d{3})+(?!\d))/g,',');};/** * 转义 HTML 特殊字符(防止 XSS) * @param {string} str 源字符串 * @returns {string} 转义后的字符串 */exportconstescapeHtml=(str)=>{if(!isString(str))returnstr;constmap={'&':'&','<':'<','>':'>','"':'"',"'":'''};returnstr.replace(/[&<>"']/g,(match)=>map[match]);};/** * 随机生成指定长度的字符串 * @param {number} [length=8] 长度 * @param {boolean} [includeNumber=true] 是否包含数字 * @param {boolean} [includeLetter=true] 是否包含字母 * @returns {string} 随机字符串 */exportconstrandomString=(length=8,includeNumber=true,includeLetter=true)=>{letchars='';if(includeLetter)chars+='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';if(includeNumber)chars+='0123456789';if(!chars)return'';letresult='';for(leti=0;i<length;i++){result+=chars[Math.floor(Math.random()*chars.length)];}returnresult;};七、工程化使用建议
1. 目录结构
建议在项目中创建src/utils/目录,按功能拆分文件:
src/ ├── utils/ │ ├── index.js # 入口文件(导出所有工具函数) │ ├── type.js # 类型判断 │ ├── array.js # 数组处理 │ ├── object.js # 对象处理 │ ├── date.js # 时间处理 │ ├── browser.js # 浏览器相关 │ └── string.js # 字符串处理2. 入口文件导出(index.js)
export*from'./type.js';export*from'./array.js';export*from'./object.js';export*from'./date.js';export*from'./browser.js';export*from'./string.js';3. 使用方式
import{formatDate,uniqueArray,storage}from'@/utils';// 示例:格式化时间console.log(formatDate(newDate(),'YYYY-MM-DD'));// 示例:数组去重constarr=[1,2,2,3];console.log(uniqueArray(arr));// [1,2,3]// 示例:本地存储storage.set('token','abc123',3600);// 1小时过期console.log(storage.get('token'));4. 注意事项
- 兼容性:工具函数中使用了 ES6+ 语法(如
padStart、Map),低版本浏览器需配 Babel/Polyfill; - 边界处理:所有函数都做了入参校验,避免传入非法值导致报错;
- 按需引入:若项目体积敏感,可单独引入某类函数(如
import { formatDate } from '@/utils/date.js'); - 单元测试:关键工具函数建议写单元测试(如 Jest),确保逻辑稳定。
以上工具函数覆盖 90%+ 日常开发场景,可根据项目需求删减/扩展。