前言
本文主要记录一些较为常见且实用的工具函数。
1. 防抖节流
防抖函数的作用是确保在事件触发 n 秒后再执行回调
函数,如果在这 n 秒内又触发了事件,则重新计时。
function debounce(func, delay) {
let timer;
return function(...args) {
clearTimeout(timer);
timer = setTimeout(() => {
func.apply(this, args);
}, delay);
};
}
节流函数的作用是确保在短时间内只触发一次事件
,如果在这个时间内触发了多次事件,只有一次事件会被执行。
function throttle(func, delay) {
let last = 0;
return function(...args) {
const now = Date.now();
if (now - last >= delay) {
func.apply(this, args);
last = now;
}
};
}
2. URL 相关
// 对象转查询(参数)字符串
export const jsonObj2QueryStr = (jsonObj, encode = true) => {
return Object.keys(jsonObj).map(k => {
if (encode) return encodeURIComponent(k) + '=' + encodeURIComponent(jsonObj[k])
return k + '=' + jsonObj[k]
}).join('&')
}
// 把json对象转换成查询参数添加进URL中
export const addUrlParams = (url, params) => {
let queryStr = jsonObj2QueryStr(params)
if (url.indexOf('?') !== -1) {
return url + '&' + queryStr
}
return url + '?' + queryStr
}
// 把 url 参数转换成对象
export const urlParams2Json = (uri) => {
let jsonObj = {}
let paramsStr = uri.split('?')[1]
if (paramsStr) {
let paramsArr = paramsStr.split('&')
paramsArr.forEach(item => {
let key = item.split('=')[0]
let val = item.split('=')[1] || ''
jsonObj[key] = val
})
}
return jsonObj
}
3. 金额转换
// 数值/数值字符串转换千分位
function toThousands(value, precision = 2) {
// 如果输入不是数值或数值字符串,直接返回原值
if (isNaN(value) || value === null || value === undefined) {
return value;
}
const num = Number(value);
// 使用 toLocaleString 方法将数值转换为千分位展示的字符串
const formatted = num.toLocaleString(undefined, {
minimumFractionDigits: precision,
maximumFractionDigits: precision
});
return formatted;
}
// 千分位字符串转数值
function thousandsToNum(str) {
// 先去除千分位的逗号
const numStr = str.replace(/,/g, '');
// 然后使用 Number 函数将字符串转换为数值
return Number(numStr);
}
// 金额数值转换成大写金额
export const transMoneyCN = (number) => {
number = number + ''
let minus = /^-\.*/.test(number)
if (minus) {
number = number.substr(1, number.length - 1)
}
let ret = ''
if (number != '' && number != null && number != '0') {
let unit = '仟佰拾亿仟佰拾万仟佰拾元角分',
str = ''
number += '00'
let point = number.indexOf('.')
if (point >= 0) {
number = number.substring(0, point) + number.substr(point + 1, 2)
}
unit = unit.substr(unit.length - number.length)
for (let i = 0; i < number.length; i++) {
str +=
'零壹贰叁肆伍陆柒捌玖'.charAt(number.charAt(i)) + unit.charAt(i)
}
ret = str.replace(/零(仟|佰|拾|角)/g, '零')
.replace(/(零)+/g, '零')
.replace(/零(万|亿|元)/g, '$1')
.replace(/(亿)万|(拾)/g, '$1$2')
.replace(/^元零?|零分/g, '')
.replace(/元$/g, '元')
if (point <= -1) {
ret += '整'
}
}
return minus ? ('负' + ret) : ret
}
4. 深拷贝
// WeakMap 解决循环引用
let map = new WeakMap()
// 深拷贝
export function deepClone(obj) {
// 区分基本类型和引用类型
if (obj instanceof Object) {
// 如果已经存储过这个对象,直接从map获取
if (map.has(obj)) {
return map.get(obj)
}
let newObj
// 细分 Object 的常见具体类型 - 函数、正则、Date对象、普通对象(含数组)
if (obj instanceof Array) {
newObj = []
} else if (obj instanceof Function) {
newObj = function () {
return obj.apply(this, arguments)
}
} else if (obj instanceof RegExp) {
newObj = new RegExp(obj.source, obj.flags)
} else if (obj instanceof Date) {
newObj = new Date(obj)
} else {
newObj = {}
}
// 克隆一份对象出来
let desc = Object.getOwnPropertyDescriptors(obj)
let clone = Object.create(Object.getPrototypeOf(obj), desc)
map.set(obj, clone)
// 处理数组/对象
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
newObj[key] = deepClone(obj[key])
}
}
return newObj
} else {
return obj
}
}
5. Storge/Session/cookie
const isJsonStr = str => {
try {
let obj = JSON.parse(str)
if (obj && typeof obj === 'object') return true
return false
} catch (e) {
return false
}
}
export const storage = {
set: (key, val) => {
if (typeof val === 'object') val = JSON.stringify(val)
localStorage.setItem(key, val)
},
get: key => {
let r = localStorage.getItem(key)
if (isJsonStr(r)) return JSON.parse(r)
return r
},
remove: key => {
localStorage.removeItem(key)
},
clear: () => {
localStorage.clear()
}
}
export const cookie = {
set: (key, val, expires = 0) => {
let cookieStr = ''
cookieStr += encodeURIComponent(key) + '=' + encodeURIComponent(val)
if (expires) {
let d = new Date()
d.setTime(d.getTime() + (expires * 1000))
cookieStr += '; expires=' + d.toGMTString()
}
cookieStr += '; path=/'
document.cookie = cookieStr
},
get: key => {
let name = encodeURIComponent(key)
let reg = new RegExp('(^| )' + name + '=([^;]*)(;|$)')
let arr = document.cookie.match(reg)
if (arr) return unescape(arr[2])
return null
},
remove: key => {
this.set(key, '', -1)
},
clear: () => {
const keys = document.cookie.match(/[^ =;]+(?==)/g)
if (keys) {
for (let i = keys.length; i--;) {
// 清除当前域名下的,例如:a.abc.com
document.cookie = keys[i] + '=0;path=/;expires=' + new Date(0).toUTCString()
document.cookie = keys[i] + '=0;path=/;domain=' + document.domain + ';expires=' + new Date(0).toUTCString()
// 清除一级域名下的或指定的,例如 .abc.com
document.cookie = keys[i] + '=0;path=/;domain=kevis.com;expires=' + new Date(0).toUTCString()
}
}
}
}
export const session = {
set: (key, val) => {
if (typeof val === 'object') val = JSON.stringify(val)
sessionStorage.setItem(key, val)
},
get: key => {
let r = sessionStorage.getItem(key)
if (isJsonStr(r)) return JSON.parse(r)
return r
},
remove: key => {
sessionStorage.removeItem(key)
},
clear: () => {
sessionStorage.clear()
}
}
6. 文件下载
// 导出文件流下载
export const fileStreamExport = (data, filename, mime) => {
let blob = new Blob([data], {type: mime || 'application/octet-stream'})
if (typeof window.navigator.msSaveBlob !== 'undefined') {
window.navigator.msSaveBlob(blob, filename)
} else {
let blobURL = window.URL?.createObjectURL ? window.URL.createObjectURL(blob) : window.webkitURL.createObjectURL(blob)
downloadFileByURL(blobURL, filename)
}
}
// 通过URL下载
export const downloadFileByURL = (url, filename) => {
// 创建a标签实现下载
const link = document.createElement('a')
link.style.display = 'none'
link.href = url
if (filename) link.setAttribute('download', filename) // 自定义文件名
if (typeof link.download === 'undefined') {
link.setAttribute('target', '_blank')
}
document.body.appendChild(link)
link.click()
// 释放内存
setTimeout(() => {
document.body.removeChild(link)
window.URL.revokeObjectURL(url)
}, 200)
}
7. 拷贝到剪切板
export const copy2clipboard = (text, el) => {
if (el) {
el.value = text
el.setSelectionRange(0, text.length)
return document.execCommand('copy')
}
let inputEl = document.querySelector('input.disguise-hide')
if (!inputEl) {
document.body.insertAdjacentHTML('beforeend', `<input class="disguise-hide" value="${text}">`)
inputEl = document.body.lastElementChild
} else {
inputEl.value = text
}
inputEl.focus()
inputEl.setSelectionRange(0, text.length)
let val = document.execCommand('copy')
inputEl.blur()
return val
}
8. Date 对象转指定格式字符串
function formatDate(date, format = 'yyyy-MM-dd') {
const map = {
'M': date.getMonth() + 1,
'd': date.getDate(),
'h': date.getHours(),
'm': date.getMinutes(),
's': date.getSeconds(),
'q': Math.floor((date.getMonth() + 3) / 3),
'S': date.getMilliseconds()
};
return format.replace(/([yMdhmsqS])+/g, (all, t) => {
let v = map[t];
if (v !== undefined) {
if (all.length > 1) {
v = '0' + v;
v = v.substr(v.length - 2);
}
return v;
} else if (t === 'y') {
return (date.getFullYear() + '').substr(4 - all.length);
}
return all;
});
}