最近居家,回顾问题解解闷。今天汇总一下过去一年用的工具类(github开源仓库,请勇猛star),顺便发个npm包:haUtil
数据处理
关于浅拷贝和深拷贝更详细的分析请查看:清晰易懂!讲解JS赋值、浅拷贝和深拷贝
浅拷贝
/**
* 浅拷贝
* @param {Object} source
* @returns {Object}
*/
function clone(source) {
const result = Object.assign({}, source);
return result;
}
module.exports = clone;
深拷贝
深拷贝的原理是一层层遍历,直到所有数据都不可继续下探
/**
* 深拷贝
* @param {*} source
* @param {WeakMap} weakMap
* @returns {*} target
*/
// 可遍历对象
const iterations = [
'[object Object]',
'[object Array]',
'[object Map]',
'[object Set]',
];
function cloneDeep(source, weakMap = new WeakMap()) {
if (source === null) return source;
// 获取对象类型
const type = Object.prototype.toString.call(source);
// 处理不可遍历对象
if (!iterations.includes(type)) {
if (type === '[object Date]') return new Date(source);
if (type === '[object RegExp]') return new RegExp(source);
if (type === '[object Symbol]') return Symbol(source.description);
if (type === '[object Function]') return source;
// 剩余的一般是原始类型,直接返回
return source;
}
// 创建 target 实例
let target = new source.constructor(); // {} | [] | Map(0) | Set(0)
// 处理循环调用
const val = weakMap.get(source);
if (val) return val;
weakMap.set(source, target);
if (type === '[object Map]') {
source.forEach((value, key) => {
target.set(key, cloneDeep(value));
});
return target;
}
if (type === '[object Set]') {
source.forEach(value => {
target.add(cloneDeep(value));
});
return target;
}
// 处理对象和数组
for (const key in source) {
target[key] = cloneDeep(source[key], weakMap);
}
return target;
}
module.exports = cloneDeep;
任意范围随机数
/**
* 生成指定范围[min, max]的随机数
* @param {Number} min
* @param {Number} max
* @return {Number}
*/
function randomNum(min, max) {
if (min >= max) return min;
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min + 1)) + min;
}
module.exports = randomNum;
数字转百分比
/**
* 转换百分比
* @param {number} num
* @param {number} total
* @returns {string} 百分比
*/
function toPercent(num, total) {
let str =
num >= 0 && total > 0 ? Number(((num / total) * 100).toFixed(2)) : 0;
return str;
}
module.exports = toPercent;
文件单位自适应
/**
* 文件大小单位适配,自动转换B、K、M、G单位
* @param {number | string} fileSize 文件大小,以B为单位
* @returns {string}
*/
function translateFileSize(fileSize) {
if (fileSize === null || typeof fileSize === 'undefined' || fileSize === '') {
return '—';
}
if (fileSize < 1024) {
fileSize = fileSize + 'B';
} else if (fileSize < 1024 * 1024) {
fileSize = Math.floor((fileSize * 10) / 1024) / 10 + 'K';
} else if (fileSize < 1024 * 1024 * 1024) {
fileSize = Math.floor((fileSize * 10) / (1024 * 1024)) / 10 + 'M';
} else {
fileSize = Math.floor((fileSize * 10) / (1024 * 1024 * 1024)) / 10 + 'G';
}
return fileSize;
}
module.exports = translateFileSize;
金额处理
金额超出三位加逗号
/**
* 金额超出三位加 ','
* @param {string} num 金额
* @param {string} locales 本地化配置,默认zh-CN
* @param {object} options 本地化配置,默认{ style: 'currency', currency: 'CNY' }
* @returns {string}
*/
function amountFormat(
num,
locales = 'zh-CN',
options = { style: 'currency', currency: 'CNY' }
) {
return num ? Number(num).toLocaleString(locales, options) : '0';
}
module.exports = amountFormat;
事件处理
防抖
/**
* 防抖
*
* 用法:
* const cbFun = debounce(fun, 2000)
* cbFun()
*/
function debounce(fn, delay = 1000) {
let timer = null;
return function() {
if (timer !== null) {
clearTimeout(timer);
}
timer = setTimeout(() => {
fn.apply(this, arguments);
}, delay);
};
}
module.exports = debounce;
节流
/**
* 节流
*
* 用法:
* const cbFun = throttle(fun, 2000)
* cbFun()
*/
function throttle(fn, delay = 1000) {
let old = 0;
return function() {
let now = new Date().valueOf();
if (now - old > delay) {
fn.apply(this, arguments);
old = now;
}
};
}
module.exports = throttle;
定时器
var requestAnimFrame = (function() {
return (
window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
function(callback) {
window.setTimeout(callback, 1000 / 60);
}
);
})();
/**
* rAF 定时器
*
* 用法:
* const cbFun = rafInterval(fun, 2000)
* cbFun()
*/
function rafInterval(fn, delay = 1000) {
let start = 0;
function fun() {
const timestamp = new Date().valueOf();
if (start === undefined) start = timestamp;
if (timestamp - start > delay) {
start = timestamp;
fn.apply(this, arguments);
}
requestAnimFrame(fun);
}
return function() {
requestAnimFrame(fun);
};
}
module.exports = rafInterval;
文件处理
导出文件
/**
* 导出文件
* @param {string} biteData 字节流
* @param {string} title 文件名
* @param {string} ext 文件后缀
*/
function downloadFile(biteData, title = '导出', ext = 'xls') {
let blob = new Blob([biteData], { type: 'application/octet-binary' });
let downloadElement = document.createElement('a');
let href = window.URL.createObjectURL(blob);
downloadElement.target = '_blank';
downloadElement.href = href;
downloadElement.download = `${title}.${ext}`; // 文件名
document.body.appendChild(downloadElement);
downloadElement.click();
document.body.removeChild(downloadElement);
window.URL.revokeObjectURL(href); // 释放掉blob对象
}
module.exports = downloadFile;
json导出csv文件
/**
* JSON 数据转成 CSV 文件导出
* @param {Array} list 要导出的JSON数据
* @param {Array} cols 表头名称,格式如:['ID', '姓名', '性别'],默认用第一条数据的key
* @param {Array} keys 表头使用的key,格式如:['id', 'name', 'gender'],需要和cols一一对应,否则导出数据有问题,默认用第一条数据的key
* @param {string} fileName 导出的文件名称,不含日期前缀和文件类型后缀的部分
*
* 用法:
* json2Csv(list, cols, keys, fileName)
*/
function json2Csv(list, cols, keys, fileName = '数据明细') {
if (
!(
list instanceof Array &&
cols instanceof Array &&
keys instanceof Array &&
typeof fileName === 'string'
)
) {
console.log('参数格式错误');
return;
}
if (list.length === 0) return;
if (cols.length === 0) cols = Object.keys(list[0]);
if (keys.length === 0) keys = Object.keys(list[0]);
fileName = fileName || '数据明细';
let title = cols;
let jsonKey = keys;
let data = list;
let str = [];
str.push(title.join(',') + '\n');
for (let i = 0; i < data.length; i++) {
let temp = [];
for (let j = 0; j < jsonKey.length; j++) {
temp.push(data[i][jsonKey[j]]);
}
str.push(temp.join(',') + '\n');
}
let uri =
'data:text/csv;charset=utf-8,\ufeff' + encodeURIComponent(str.join(''));
let downloadLink = document.createElement('a');
downloadLink.href = uri;
downloadLink.download =
new Date().toISOString().substring(0, 10) + '-' + fileName + '.csv';
document.body.appendChild(downloadLink);
downloadLink.click();
document.body.removeChild(downloadLink);
}
module.exports = json2Csv;
正则校验
邮箱
/**
* 判断邮箱格式
* @param {string} str
* @returns {Boolean}
*/
function isEmailNum(str, type = 1) {
return /^[A-Za-z0-9_\u4e00-\u9fa5]+@[a-zA-Z0-9_-]+(.[a-zA-Z0-9_-]+)+$/.test(
str
);
}
module.exports = isEmailNum;
身份证
/**
* 判断是否为身份证号
* @param {string | number} str
* @returns {Boolean}
*/
function isIdCard(str) {
return /^(^[1-9]\d{7}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])\d{3}$)|(^[1-9]\d{5}[1-9]\d{3}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])((\d{4})|\d{3}[Xx])$)$/.test(
str
);
}
module.exports = isIdCard;
手机号码
/**
* 判断是否为电话号码,会过滤掉空格和横杠
* @param {string | number} str 手机号码
* @returns {Boolean}
*/
function isPhoneNum(str, type = 1) {
str = str
.replace(/ /g, '')
.replace(/-/g, '')
.replace(/\(|\)|\(|\)/g, ''); // 去除空格、横杠和括号
return /^(\+?0?86\-?)?1[3456789]\d{9}$/.test(str);
}
module.exports = isPhoneNum;
座机号码
/**
* 判断是否为电话号码,会过滤掉空格和横杠
* @param {string | number} str 电话号码
* @returns {Boolean}
*/
function isPhoneNum(str, type = 1) {
str = str
.replace(/ /g, '')
.replace(/-/g, '')
.replace(/\(|\)|\(|\)/g, ''); // 去除空格、横杠和括号
return /^(\d{3,4})?[0-9]{7,8}$/.test(str);
}
module.exports = isPhoneNum;
以上仅仅是根据自己业务需求整理的,不一定适用所有项目,如有问题,欢迎指正~