@ivu-plus/i-utils
Version:
前端模块化 JavaScript 工具库
2,256 lines (2,076 loc) • 452 kB
JavaScript
/*
* @ivu-plus/i-utils v1.0.8
* Copyright 2021-2025, <gao911222@163.com>
* Released under the MIT License.
*/
'use strict';
/**
* 排序
*/
const SORT = {
// 降序
DESC: 0,
// 升序
ASC: 1,
// 随机排序
RANDOM: 2,
};
/**
* 数学
*/
const MATH = {
// 正常四舍五入,如:1.354保留两位是1.35;1.355保留两位是1.36;
ROUND: 0,
// 向下舍出,如:1.354保留两位是1.35;1.355保留两位是1.35;
ROUND_FLOOR: 1,
};
/**
* 语言
*/
const LANG = {
// 中文
ZH: "zh",
// 英文
EN: "en",
};
/**
* 正则
*/
const REGEXP = {
// 中文汉字
CH: /^[\u4E00-\u9FA5]+$/,
// 英文字母
EN: /^[a-zA-Z]$/,
// 小写字母
LOWER_CASE: /^[a-z]+$/,
// 大写字母
UPPER_CASE: /^[A-Z]+$/,
// 中文姓名,2-16位
CH_NAME: /^[\u4e00-\u9fa5·]{2,16}$/,
// 英文姓名,0-20位
EN_NAME: /(^[a-zA-Z][a-zA-Z\s]{0,20}[a-zA-Z]$)/,
// 数字,包含正数和负数
NUMBER: /^([-+])?\d+(\.\d+)?$/,
// 整数,包含:0,正整数和负整数
INTEGER: /^(0|[1-9][0-9]*|-[1-9][0-9]*)$/,
// 小数,包含正小数和负小数
DECIMAL: /^\d+\.\d+$/,
// 正整数或者保留两位小数
INT_OR_FLOAT: /(^[1-9][0-9]*$)|(^[1-9][0-9]*\.[0-9]{1,2}$)|(^0\.[0-9]{1,2}$)|(^0$)/,
// 手机号码,支持+86
MOBILE: /^(?:(?:\+|00)86)?1[1-9]\d{9}$/,
// 固定电话号码,比如:0755-1111111
PHONE: /^(?:(?:\d{3}-)?\d{8}|^(?:\d{4}-)?\d{7,8})(?:-\d+)?$/,
// 邮箱
EMAIL: /^[\w-]+(\.[\w-]+)*@[\w-]+(\.[\w-]+)+$/,
// 一代15位和二代18位身份证
ID_CARD:
/(^\d{8}(0\d|10|11|12)([0-2]\d|30|31)\d{3}$)|(^\d{6}(18|19|20)\d{2}(0[1-9]|10|11|12)([0-2]\d|30|31)\d{3}(\d|X|x)$)/,
// 仅校验一代15位身份证
ID_CARD15: /(^\d{8}(0\d|10|11|12)([0-2]\d|30|31)\d{3}$)/,
// 仅校验二代18位身份证
ID_CARD18: /(^\d{6}(18|19|20)\d{2}(0[1-9]|10|11|12)([0-2]\d|30|31)\d{3}(\d|X|x)$)/,
// 银行卡号
BANK_CARD: /^[1-9]\d{9,29}$/,
// 邮政编码
POST_CODE: /^(0[1-7]|1[0-356]|2[0-7]|3[0-6]|4[0-7]|5[1-7]|6[1-7]|7[0-5]|8[013-6])\d{4}$/,
// url地址,可以获取到主机地址、主机名、端口号、协议、查询参数、hash等信息
URL: /^(?:(ftp|https?|ftps):\/\/)?((?:\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}|[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)+(?:[a-zA-Z]{2,6})?)?(?::(\d+))?(?:\/([^\?#]*))?(?:\?([^#]*))?(?:#(.*))?$/,
// ip地址
IP: /^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/,
// IPv6地址
IP6: /^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$/,
// 是外链,http,https,mail,tel电话
EXTERNAL: /^(http?:|https?:|mailto:|tel:)/,
// 是函数字符串,包括async异步函数和箭头函数
FUNCTION_STR:
/(function\([\w,\s]*\)\s*{[\s\S]*})|\(\s*([\w,\s]*)\s*\)\s*=>\s*{[\s\S]*}|(async\s*function\s*\([\w,\s]*\)\s*{[\s\S]*}\)\s*{[\s\S]*})|(async\(\s*([\w,\s]*)\s*\)\s*=>\s*{[\s\S]*})$/,
};
/**
* 日期
*/
const DATE = {
// 上午和下午
AM_PM: {
zh: {
AM: "上午",
PM: "下午",
},
en: {
AM: "AM",
PM: "PM",
},
},
// 周
WEEK: {
zh: {
FULL: ["星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六"],
SHORT: ["周日", "周一", "周二", "周三", "周四", "周五", "周六"],
MINI: ["日", "一", "二", "三", "四", "五", "六"],
},
en: {
FULL: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"],
SHORT: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
MINI: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
},
},
// 月
MONTH: {
zh: {
FULL: ["一月", "二月", "三月", "四月", "五月", "六月", "七月", "八月", "九月", "十月", "十一月", "十二月"],
SHORT: ["一", "二", "三", "四", "五", "六", "七", "八", "九", "十", "十一", "十二"],
},
en: {
FULL: [
"January",
"February",
"March",
"April",
"May",
"June",
"July",
"August",
"September",
"October",
"November",
"December",
],
SHORT: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
},
},
// 季度
QUARTER: {
zh: {
FULL: ["第一季度", "第二季度", "第三季度", "第四季度"],
SHORT: ["一季度", "二季度", "三季度", "四季度"],
MINI: ["一", "二", "三", "四"],
},
en: {
FULL: ["quarter 1st", "quarter 2nd", "quarter 3rd", "quarter 4th"],
SHORT: ["Q1th", "Q2nd", "Q3rd", "Q4th"],
MINI: ["Q1", "Q2", "Q3", "Q4"],
},
},
// 已过日期类型
OVER_TIME: {
zh: {
YEAR: "年",
MONTH: "月",
DATE: "日",
HOUR: "时",
MINUTE: "分",
SECOND: "秒",
MILLISECOND: "毫秒",
DAY: "天",
QUARTER: "季度",
},
en: {
YEAR: "year",
MONTH: "month",
DATE: "date",
HOUR: "hour",
MINUTE: "minute",
SECOND: "second",
MILLISECOND: "millisecond",
DAY: "day",
QUARTER: "quarter",
},
},
// 时间节点
PASS_TIME: {
zh: {
YEAR: "年前",
MONTH: "个月前",
DAY: "天前",
BEFORE_YESTERDAY: "前天",
YESTERDAY: "昨天",
TODAY: "今天",
HOUR: "小时前",
MINUTE: "分钟前",
JUST: "刚刚",
},
en: {
YEAR: " year ago",
MONTH: " month ago",
DAY: " day ago",
BEFORE_YESTERDAY: "before yesterday",
YESTERDAY: " yesterday",
TODAY: " today",
HOUR: " hour ago",
MINUTE: " minute ago",
JUST: " just",
},
},
// 节假日
HOLIDAY: {
zh: ["元旦", "春节", "清明节", "劳动节", "端午节", "中秋节", "国庆节"],
en: [
"New Year‘s Day",
"Spring Festival",
"Tomb Sweeping Day",
"Labor Day",
"Dragon Boat Festival",
"Mid-Autumn Day",
"National Day",
],
},
// 星座
ZODIAC: {
zh: [
"摩羯座",
"水瓶座",
"双鱼座",
"白羊座",
"金牛座",
"双子座",
"巨蟹座",
"狮子座",
"处女座",
"天秤座",
"天蝎座",
"射手座",
],
en: [
"Capricorn",
"Aquarius",
"Pisces",
"Aries",
"Taurus",
"Gemini",
"Cancer",
"Leo",
"Virgo",
"Libra",
"Scorpio",
"Sagittarius",
],
},
// 生肖
CHINESE_ZODIAC: {
zh: ["鼠", "牛", "虎", "兔", "龙", "蛇", "马", "羊", "猴", "鸡", "狗", "猪"],
en: ["Rat", "Ox", "Tiger", "Rabbit", "Dragon", "Snake", "Horse", "Goat", "Monkey", "Rooster", "Dog", "Pig"],
},
// 天干地支
HEAVENLY_STEMS: {
zh: ["甲", "乙", "丙", "丁", "戊", "己", "庚", "辛", "壬", "癸"],
en: ["Jia", "Yi", "Bing", "Ding", "Wu", "Ji", "Geng", "Xin", "Ren", "Gui"],
},
EARTHLY_BRANCHES: {
zh: ["子", "丑", "寅", "卯", "辰", "巳", "午", "未", "申", "酉", "戌", "亥"],
en: ["Zi", "Chou", "Yin", "Mao", "Chen", "Si", "Wu", "Wei", "Shen", "You", "Xu", "Hai"],
},
};
/*
* 键盘码
*/
const KEYCODE = {
// 字符键
8: "Backspace",
9: "Tab",
13: "Enter",
16: "Shift",
17: "Ctrl",
18: "Alt",
19: "Pause",
20: "Caps Lock",
27: "Escape",
32: "Space",
33: "Page Up",
34: "Page Down",
35: "End",
36: "Home",
37: "Left",
38: "Up",
39: "Right",
40: "Down",
42: "Print Screen",
45: "Insert",
46: "Delete",
// 数字键
48: "0",
49: "1",
50: "2",
51: "3",
52: "4",
53: "5",
54: "6",
55: "7",
56: "8",
57: "9",
// 字母键
65: "A",
66: "B",
67: "C",
68: "D",
69: "E",
70: "F",
71: "G",
72: "H",
73: "I",
74: "J",
75: "K",
76: "L",
77: "M",
78: "N",
79: "O",
80: "P",
81: "Q",
82: "R",
83: "S",
84: "T",
85: "U",
86: "V",
87: "W",
88: "X",
89: "Y",
90: "Z",
// window
91: "Windows",
93: "Right Click",
// Numpad
96: "Numpad 0",
97: "Numpad 1",
98: "Numpad 2",
99: "Numpad 3",
100: "Numpad 4",
101: "Numpad 5",
102: "Numpad 6",
103: "Numpad 7",
104: "Numpad 8",
105: "Numpad 9",
106: "Numpad *",
107: "Numpad +",
109: "Numpad -",
110: "Numpad .",
111: "Numpad /",
// F功能键
112: "F1",
113: "F2",
114: "F3",
115: "F4",
116: "F5",
117: "F6",
118: "F7",
119: "F8",
120: "F9",
121: "F10",
122: "F11",
123: "F12",
// 特殊符号
144: "Num Lock",
145: "Scroll Lock",
182: "My Computer",
183: "My Calculator",
186: ";",
187: "=",
188: ",",
189: "-",
190: ".",
191: "/",
192: "`",
219: "[",
220: "\\",
221: "]",
222: "'",
};
/**
* 身份证号
*/
const ID_CARD = {
// 身份证号对应的省
PROVINCE: {
11: "北京",
12: "天津",
13: "河北",
14: "山西",
15: "内蒙古",
21: "辽宁",
22: "吉林",
23: "黑龙江",
31: "上海",
32: "江苏",
33: "浙江",
34: "安徽",
35: "福建",
36: "江西",
37: "山东",
41: "河南",
42: "湖北",
43: "湖南",
44: "广东",
45: "广西",
46: "海南",
50: "重庆",
51: "四川",
52: "贵州",
53: "云南",
54: "西藏",
61: "陕西",
62: "甘肃",
63: "青海",
64: "宁夏",
65: "新疆",
71: "台湾",
81: "香港",
82: "澳门",
91: "国外",
},
// 性别
SEX: {
MAN: "男",
WOMAN: "女",
},
};
/* 字符串处理 */
/**
* 字符串中是否包含指定的元素
* @param {String} value 包含的元素
* @param {String} str 查找的字符串
* @returns {Boolean} 返回true和false
*/
function inString(value, str) {
return str.includes(value);
}
/**
* 去除字符串前后位置空格
* @param {String} value 参数
* @returns {String} 返回处理后的字符串
*/
function trim(value) {
return value.replace(/(^\s*)|(\s*$)/g, "");
}
/**
* 去除字符串开始位置的空格
* @param {String} value 参数
* @returns {String} 返回处理后的字符串
*/
function trimStart(value) {
return value.replace(/(^\s*)/g, "");
}
/**
* 去除字符串结束位置的空格
* @param {String} value 参数
* @returns {String} 返回处理后的字符串
*/
function trimEnd(value) {
return value.replace(/(\s*$)/g, "");
}
/**
* 去除字符串中全部的空格
* @param {String} value 参数
* @returns {String} 返回处理后的字符串
*/
function trimAll(value) {
return value.replace(/\s+/g, "");
}
/**
* 替换所有指定字符串为新的字符串
* @param {String} value 参数
* @param {String} oldSubstr 需要替换的字符串
* @param {String} newSubstr 替换后的字符串
* @returns {String} 返回处理后的字符串
*/
function replaceAll(value, oldSubstr, newSubstr) {
return value.replace(new RegExp(oldSubstr, "gm"), newSubstr);
}
/* 字符串转换 */
/**
* 字符串转大写
* @param {String} value 参数
* @returns {String} 返回处理后的字符串
*/
function toUpperCase(value) {
return String(value).toLocaleUpperCase();
}
/**
* 字符串转小写
* @param {String} value 参数
* @returns {String} 返回处理后的字符串
*/
function toLowerCase(value) {
String(value).toLocaleLowerCase();
}
/**
* 转为 snake_case 下划线命名
* @description 支持 驼峰命名,短横命名,帕斯卡命名
* @param {String} value 参数
* @returns {String} 返回处理后的字符串
*/
function toSnakeCase(value) {
// 驼峰
if (/^[a-z]$/.test(value.charAt(0)) && !(value.indexOf("-") > 0 || value.indexOf("_") > 0)) {
return value.replace(/([A-Z])/g, "_$1").toLowerCase();
}
// 短横
if (value.indexOf("-") > 0) {
return value.replace(/-/g, "_").toLowerCase();
}
// 帕斯卡
if (/^[A-Z]$/.test(value.charAt(0)) && !(value.indexOf("-") > 0 || value.indexOf("_") > 0)) {
value = value.charAt(0).toLowerCase() + value.slice(1);
return value.replace(/([A-Z])/g, "_$1").toLowerCase();
}
}
/**
* 转为 kebab-case 短横命名
* @description 支持 下划线,驼峰命名,帕斯卡命名
* @param {String} value 参数
* @returns {String} 返回处理后的字符串
*/
function toKebabCase(value) {
// 下划线
if (value.indexOf("_") > 0) {
return value.replace(/_/g, "-").toLowerCase();
}
// 驼峰
if (/^[a-z]$/.test(value.charAt(0)) && !(value.indexOf("-") > 0 || value.indexOf("_") > 0)) {
return value.replace(/([A-Z])/g, "-$1").toLowerCase();
}
// 帕斯卡
if (/^[A-Z]$/.test(value.charAt(0)) && !(value.indexOf("-") > 0 || value.indexOf("_") > 0)) {
let newStr = value.charAt(0).toLowerCase() + value.slice(1);
return newStr.replace(/([A-Z])/g, "-$1").toLowerCase();
}
}
/**
* 转为 camelCase 驼峰命名
* @description 支持 下划线命名,短横命名,帕斯卡命名
* @param {String} value 参数
* @returns {String} 返回处理后的字符串
*/
function toCamelCase(value) {
// 下划线
if (value.indexOf("_") > 0) {
return value.replace(/\_(\w)/g, function (all, letter) {
return letter.toUpperCase();
});
}
// 短横
else if (value.indexOf("-") > 0) {
return value.replace(/\-(\w)/g, function (all, letter) {
return letter.toUpperCase();
});
}
// 帕斯卡
else if (/^[A-Z]$/.test(value.charAt(0)) && !(value.indexOf("-") > 0 || value.indexOf("_") > 0)) {
return value.charAt(0).toLowerCase() + value.slice(1);
}
// 返回自身
else {
return value;
}
}
/**
* 转为 PascalCase 帕斯卡命名
* @description 支持 下划线命名,短横命名,驼峰命名
* @param {String} value 参数
* @returns {String} 返回处理后的字符串
*/
function toPascalCase(value) {
// 下划线
if (value.indexOf("_") > 0) {
let newStr = value.replace(/\_(\w)/g, function (all, letter) {
return letter.toUpperCase();
});
return newStr.charAt(0).toUpperCase() + newStr.slice(1);
}
// 短横
else if (value.indexOf("-") > 0) {
let newStr = value.replace(/\-(\w)/g, function (all, letter) {
return letter.toUpperCase();
});
return newStr.charAt(0).toUpperCase() + newStr.slice(1);
}
// 驼峰
else if (/^[a-z]$/.test(value.charAt(0)) && !(value.indexOf("-") > 0 || value.indexOf("_") > 0)) {
return value.charAt(0).toUpperCase() + value.slice(1);
}
// 返回自身
else {
return value;
}
}
/* 字符串格式化 */
/**
* 数字前补齐0达到指定位数
* @description 相当于原生的 padStart(2,'0')
* @param {Number|String} value 补零的数字
* @param {Number} maxLength 补齐0后的最大长度,默认2位
* @returns {String} 返回补0后指定位数的字符串
*/
function zeroStart(value, maxLength = 2) {
let len = value.toString().length;
while (len < maxLength) {
value = "0" + value;
len++;
}
return value;
}
/**
* 数字后补齐0达到指定位数
* @description 相当于原生的 padEnd(2,'0')
* @param {Number|String} value 补零的数字
* @param {Number} maxLength 补齐0后的最大长度,默认2位
* @returns {String} 返回补0后指定位数的字符串
*/
function zeroEnd(value, maxLength = 2) {
let len = value.toString().length;
while (len < maxLength) {
value = value + "0";
len++;
}
return value;
}
/**
* 格式化为标题样式
* @param {String} value 字符串值
* @returns {String} 返回格式化后的标题样式
*/
function formatTitle(value) {
if (!value) return "";
return value
.split(/(?=[A-Z])|[\.\-\s_]/)
.map((s) => s.trim())
.filter((s) => !!s)
.map((s) => toPascalCase(s.toLowerCase()))
.join(" ");
}
/**
* 格式化字符串模版
* @param {String} value 字符串值
* @param {Object} data 模版数据
* @returns {String} 返回格式化后的模版字符串
*/
function formatTemplate(value, data) {
if (!value) return "";
return Array.from(value.matchAll(/\{\{(.+?)\}\}/g)).reduce((acc, match) => {
return acc.replace(match[0], data[match[1]]);
}, value);
}
/**
* 格式化千分位数字
* @description 支持任意数据传参,如果非数字则不会格式化,并返回原数据
* @param {Number|String} num 数字
* @returns {String} 返回格式化后的千分位数字
*/
function formatThousand(num) {
if (!parseFloat(num)) return num;
num = String(num);
let regex = num.indexOf(".") > -1 ? /(\d)(?=(\d{3})+\.)/g : /(\d)(?=(?:\d{3})+$)/g;
return num.replace(regex, "$1,");
}
/**
* 格式化人民币金额大写
* @param {Number|String} money 金额
* @returns {String} 返回金额大写
*/
function formatRmbChinese(money) {
// 汉字的数字
let cnNums = ["零", "壹", "贰", "叁", "肆", "伍", "陆", "柒", "捌", "玖"];
// 基本单位
let cnIntRadiance = ["", "拾", "佰", "仟"];
// 对应整数部分扩展单位
let cnIntUnits = ["", "万", "亿", "兆"];
// 对应小数部分单位
let cnDecUnits = ["角", "分", "毫", "厘"];
// 整数金额时后面跟的字符
let cnInteger = "整";
// 整型完以后的单位
let cnIntLast = "元";
// 最大处理的数字
let maxNum = Number("999999999999999.9999");
// 金额整数部分
let integerNum;
// 金额小数部分
let decimalNum;
// 输出的中文金额字符串
let chineseStr = "";
// 分离金额后用的数组,预定义
let parts;
if (money === "") {
// 不能用==
return "";
}
money = parseFloat(money);
if (money >= maxNum) {
// 超出最大处理数字,抛出异常
throw new Error("Calculated number overflow!");
}
if (money === 0) {
chineseStr = cnNums[0] + cnIntLast + cnInteger;
return chineseStr;
}
// 转换为字符串
money = money.toString();
if (money.indexOf(".") === -1) {
integerNum = money;
decimalNum = "";
} else {
parts = money.split(".");
integerNum = parts[0];
decimalNum = parts[1].substr(0, 4);
}
// 获取整型部分转换
if (parseInt(integerNum, 10) > 0) {
let zeroCount = 0;
let IntLen = integerNum.length;
for (let i = 0; i < IntLen; i++) {
let n = integerNum.substr(i, 1);
let p = IntLen - i - 1;
let q = p / 4;
let m = p % 4;
if (n === "0") {
zeroCount++;
} else {
if (zeroCount > 0) {
chineseStr += cnNums[0];
}
// 归零
zeroCount = 0;
chineseStr += cnNums[parseInt(n)] + cnIntRadiance[m];
}
if (m === 0 && zeroCount < 4) {
chineseStr += cnIntUnits[q];
}
}
chineseStr += cnIntLast;
}
// 小数部分
if (decimalNum !== "") {
let decLen = decimalNum.length;
for (let i = 0; i < decLen; i++) {
let n = decimalNum.substr(i, 1);
if (n !== "0") {
chineseStr += cnNums[Number(n)] + cnDecUnits[i];
}
}
}
if (chineseStr === "") {
chineseStr += cnNums[0] + cnIntLast + cnInteger;
} else if (decimalNum === "") {
chineseStr += cnInteger;
}
return chineseStr;
}
/**
* 判断是整数
* @param {*} value 参数
* @returns {Boolean} result 返回结果
*/
function isInteger(value) {
return Number.isInteger(value);
}
/**
* 判断是小数
* @param {*} value 参数
* @returns {Boolean} result 返回结果
*/
function isDecimal(value) {
return /^\d+\.\d+$/.test(value);
}
/**
* 判断类型是数字 Number
* @param {Number} value 参数
* @returns {Boolean} 返回结果
*/
function isNumber(value) {
return Object.prototype.toString.call(value).slice(8, -1) === "Number";
}
/**
* 判断类型是字符串 String
* @param {*} value 参数
* @returns {Boolean} 返回结果
*/
function isString(value) {
return Object.prototype.toString.call(value).slice(8, -1) === "String";
}
/**
* 判断类型是数组 Array
* @param {*} value 参数
* @returns {Boolean} 返回结果
*/
function isArray$4(value) {
return Object.prototype.toString.call(value).slice(8, -1) === "Array";
}
/**
* 判断类型是对象 Object
* @param {*} value 参数
* @returns {Boolean} 返回结果
*/
function isObject(value) {
return Object.prototype.toString.call(value).slice(8, -1) === "Object";
}
/**
* 判断类型是布尔 Boolean
* @param {*} value 参数
* @returns {Boolean} 返回结果
*/
function isBoolean(value) {
return Object.prototype.toString.call(value).slice(8, -1) === "Boolean";
}
/**
* 判断类型是日期 Date
* @param {*} value 参数
* @returns {Boolean} 返回结果
*/
function isDate(value) {
return Object.prototype.toString.call(value).slice(8, -1) === "Date";
}
/**
* 判断类型是函数 Function
* @param {*} value 参数
* @returns {Boolean} 返回结果
*/
function isFunction(value) {
return Object.prototype.toString.call(value).slice(8, -1) === "Function";
}
/**
* 判断类型是函数字符串 FunctionString
* @description 支持普通函数,异步函数,箭头函数
* @param {*} value 参数
* @returns {Boolean} 返回结果
*/
function isFunctionString(value) {
let isFnStr = REGEXP.FUNCTION_STR.test(value);
if (isFnStr) {
try {
new Function(`return ${value}`);
return true;
} catch (e) {
// 如果发生错误,比如语法错误,那么这不是一个有效的函数
return false;
}
} else {
return false;
}
}
/**
* 判断类型是异步函数 AsyncFunction
* @param {*} value 参数
* @returns {Boolean} 返回结果
*/
function isAsyncFunction(value) {
return Object.prototype.toString.call(value).slice(8, -1) === "AsyncFunction";
}
/**
* 判断类型是 Symbol
* @param {*} value 参数
* @returns {Boolean} 返回结果
*/
function isSymbol(value) {
return Object.prototype.toString.call(value).slice(8, -1) === "Symbol";
}
/**
* 判断类型是正则 RegExp
* @param {*} value 参数
* @returns {Boolean} 返回结果
*/
function isRegExp(value) {
return Object.prototype.toString.call(value).slice(8, -1) === "RegExp";
}
/**
* 判断类型是错误 Error
* @param {*} value 参数
* @returns {Boolean} 返回结果
*/
function isError(value) {
return Object.prototype.toString.call(value).slice(8, -1) === "Error";
}
/**
* 判断类型是 Promise
* @param {*} value 参数
* @returns {Boolean} 返回结果
*/
function isPromise(value) {
return Object.prototype.toString.call(value).slice(8, -1) === "Promise";
}
/**
*判断类型是 Map
* @param {*} value 参数
* @returns {Boolean} 返回结果
*/
function isMap(value) {
return Object.prototype.toString.call(value).slice(8, -1) === "Map";
}
/**
* 判断类型是 WeakMap
* @param {*} value 参数
* @returns {Boolean} 返回结果
*/
function isWeakMap(value) {
return Object.prototype.toString.call(value).slice(8, -1) === "WeakMap";
}
/**
* 判断类型是 Set
* @param {*} value 参数
* @returns {Boolean} 返回结果
*/
function isSet(value) {
return Object.prototype.toString.call(value).slice(8, -1) === "Set";
}
/**
* 判断类型是 WeakSet
* @param {*} value 参数
* @returns {Boolean} 返回结果
*/
function isWeakSet(value) {
return Object.prototype.toString.call(value).slice(8, -1) === "WeakSet";
}
/**
* 判断类型是 BigInt
* @param {*} value 参数
* @returns {Boolean} 返回结果
*/
function isBigInt(value) {
return Object.prototype.toString.call(value).slice(8, -1) === "BigInt";
}
/**
* 判断类型是 Json
* @param {*} value 参数
* @returns {Boolean} 返回结果
*/
function isJson(value) {
try {
JSON.parse(value);
return true;
} catch (error) {
return false;
}
}
/* 数据值校验 */
/**
* 判断值为真
* @param {*} value 校验的参数
* @returns {Boolean} 返回结果
*/
function isTrue(value) {
return !isFalse(value);
}
/**
* 判断值为假
* @param {*} value 校验的参数
* @returns {Boolean} 返回结果
*/
function isFalse(value) {
return (
value === undefined ||
value === null ||
value === "undefined" ||
value === "null" ||
value === 0 ||
value === false ||
isNaN(value)
);
}
/**
* 判断非数字
* @param {*} value 参数
* @returns {Boolean} 返回结果
*/
function isNaN(value) {
// window的isNaN函数是有缺陷的,空数组/数组有一个元素,null,空字符串 都会被认为是数字
return window.isNaN(value) || isArray$4(value) || value == null || value === "";
}
/**
* 判断是数字
* @description 等同于isNumber()
* @param {*} value 参数
* @returns {Boolean} 返回结果
*/
function isNotNaN(value) {
return !isNaN(value);
}
/**
* 判断对象为空
* @description 判断值是否为空,如果对象初始化了值则不为空
* @param {*} value 校验的参数
* @returns {Boolean} 返回结果
*/
function isNull(value) {
return value === undefined || value === null || value === "";
}
/**
* 判断对象不为空
* @description 判断值是否为空,如果对象初始化了值则不为空
* @param {*} value 校验的参数
* @returns {Boolean} 返回结果
*/
function isNotNull(value) {
return !isNull(value);
}
/**
* 判断值为空
* @description 判断是否是有意义不为空的值,如果值是{},[]空的数据则为空
* @param {*} value 校验的参数
* @returns {Boolean} 返回结果
*/
function isEmpty(value) {
return isNull(value) || !(Object.keys(value) || value).length;
}
/**
* 判断值不为空
* @description 判断是否是有意义不为空的值,如果值是{},[]空的数据则为空
* @param {*} value 校验的参数
* @returns {Boolean} 返回结果
*/
function isNotEmpty(value) {
return !isEmpty(value);
}
/**
* 判断值是空白的
* @description 同时会校验空值,空对象,以及空白符号
* @param {*} value 校验的参数
* @returns {Boolean} 返回结果
*/
function isBlank(value) {
return isEmpty(value) || /^\s*$/.test(value);
}
/**
* 判断值不是空白的
* @description 同时会校验空值,空对象,以及空白符号
* @param {*} value 校验的参数
* @returns {Boolean} 返回结果
*/
function isNotBlank(value) {
return !isBlank(value);
}
/**
* 判断值是undefined
* @param {*} value 校验的参数
* @returns {Boolean} 返回结果
*/
function isUndefined(value) {
return value === undefined;
}
/**
* 判断值不是undefined
* @param {*} value 校验的参数
* @returns {Boolean} 返回结果
*/
function isNotUndefined(value) {
return !isUndefined(value);
}
/* 数据比较 */
/**
* 判断两个值是否相等
* @param {String|Number} value1 参数1
* @param {String|Number} value2 参数2
* @returns {Boolean} 返回结果
*/
function equals(value1, value2) {
return Object.is(value1, value2);
}
/**
* 判断两个值是否相等(忽略大小写)
* @param {String|Number} value1 参数1
* @param {String|Number} value2 参数2
* @returns {Boolean} 返回结果
*/
function equalsIgnoreCase(value1, value2) {
return Object.is(value1.toLowerCase(), value2.toLowerCase());
}
/**
* 深度对比数据
* @description 可以对比任意数据,对象,数组,日期等也可深度对比,对象不区分先后顺序
* @param {*} x 数据1
* @param {*} y 数据2
* @returns {Boolean} 返回结果
*/
function deepCompare(x, y) {
let i, l, leftChain, rightChain;
function compare2Objects(x, y) {
let p;
// remember that NaN === NaN returns false
// and isNaN(undefined) returns true
if (isNaN(x) && isNaN(y) && typeof x === "number" && typeof y === "number") {
return true;
}
// Compare primitives and functions.
// Check if both arguments link to the same object.
// Especially useful on the step where we compare prototypes
if (x === y) {
return true;
}
// Works in case when functions are created in constructor.
// Comparing dates is a common scenario. Another built-ins?
// We can even handle functions passed across iframes
if (
(typeof x === "function" && typeof y === "function") ||
(x instanceof Date && y instanceof Date) ||
(x instanceof RegExp && y instanceof RegExp) ||
(x instanceof String && y instanceof String) ||
(x instanceof Number && y instanceof Number)
) {
return x.toString() === y.toString();
}
// At last checking prototypes as good as we can
if (!(x instanceof Object && y instanceof Object)) {
return false;
}
if (x.isPrototypeOf(y) || y.isPrototypeOf(x)) {
return false;
}
if (x.constructor !== y.constructor) {
return false;
}
if (x.prototype !== y.prototype) {
return false;
}
// Check for infinitive linking loops
if (leftChain.indexOf(x) > -1 || rightChain.indexOf(y) > -1) {
return false;
}
// Quick checking of one object being a subset of another.
for (p in y) {
if (y.hasOwnProperty(p) !== x.hasOwnProperty(p)) {
return false;
} else if (typeof y[p] !== typeof x[p]) {
return false;
}
}
for (p in x) {
if (y.hasOwnProperty(p) !== x.hasOwnProperty(p)) {
return false;
} else if (typeof y[p] !== typeof x[p]) {
return false;
}
switch (typeof x[p]) {
case "object":
case "function":
leftChain.push(x);
rightChain.push(y);
if (!compare2Objects(x[p], y[p])) {
return false;
}
leftChain.pop();
rightChain.pop();
break;
default:
if (x[p] !== y[p]) {
return false;
}
break;
}
}
return true;
}
if (arguments.length < 1) {
return true; // Die silently? Don't know how to handle such case, please help...
// throw "Need two or more arguments to compare";
}
for (i = 1, l = arguments.length; i < l; i++) {
leftChain = [];
rightChain = [];
if (!compare2Objects(arguments[0], arguments[i])) {
return false;
}
}
return true;
}
/**
* 转为数字类型
* @description 解决部分浏览器在转换 '08','09'等是0开头时被默认转8进制问题
* @param {String,number} value 转换的值
* @param {Number} radix 进制数,默认10进制
* @returns {Number} 返回转换后的数字
*/
function parseInt$1(value, radix = 10) {
if (isNull(value)) {
return 0;
}
return Number.parseInt(value, radix);
}
/**
* 转为小数类型
* @param {String,number} value 转换的值
* @returns {Number} 返回转换后的数字
*/
function parseFloat$1(value) {
if (isNull(value)) {
return 0.0;
}
return Number.parseFloat(value);
}
/* 数组计算 */
/**
* 数组最小值
* @param {Array} array 数组
* @returns {Number} 返回最小值
*/
function arrayMin(array) {
return Math.min.apply(null, array);
}
/**
* 数组最大值
* @param {Array} array 数组
* @returns {Number} 返回最大值
*/
function arrayMax(array) {
return Math.max.apply(null, array);
}
/**
* 数组求和
* @param {Array} array 数组
* @returns {Number} 返回和
*/
function arraySum(array) {
return array.reduce(function (pre, cur) {
return pre + cur;
});
}
/**
* 数组求平均值
* @param {Array} array 数组
* @returns {Number} 返回平均数
*/
function arrayAvg(array) {
return arraySum(array) / array.length;
}
/* 数组比较 */
/**
* 数组中是否包含指定的元素
* @param {String|Number} value 元素
* @param {Array} array 查找的数组
* @returns {Boolean} 返回结果
*/
function inArray(value, array) {
if (isNull(value)) return false;
return array.includes(value);
}
/**
* 比较两个数组是否相等
* @param {Array} array1 数组1
* @param {Array} array2 数组2
* @returns {Boolean} 返回结果
*/
function arrayEquals(array1, array2) {
if (array1 === array2) return true;
if (array1.length !== array2.length) return false;
return array1.every((v, i) => v === array2[i]);
}
/* 数组操作 */
/**
* 生成指定长度的数组
* @param {Number} length 长度,默认 0
* @returns {Array} 返回数组
*/
function arrayCreate(length = 0) {
return [...Array(length).keys()];
}
/**
* 数组指定位置添加元素
* @description 如果数组为空,则在0位置添加元素
* @param {Array} array 数组
* @param {Number} index 下标位置,默认0
* @param {*} value 添加的元素
* @returns {Array} 返回操作后的数组
*/
function arrayInsert(array = [], index = 0, value = undefined) {
if (index < 0) return array;
if (array.length === 0) {
array.push(value);
} else {
if (index > array.length - 1) {
return array;
}
array.splice(index, 0, value);
}
return array;
}
/**
* 数组指定位置前面添加元素
* @description 如果数组为空,则在0位置添加元素
* @param {Array} array 数组
* @param {Number} index 下标位置,默认0
* @param {*} value 添加的元素
* @returns {Array} 返回操作后的数组
*/
function arrayInsertBefore(array = [], index = 0, value = undefined) {
if (index < 0) return array;
if (array.length === 0) {
array.push(value);
} else {
if (index > array.length - 1) {
return array;
}
array.splice(index, 0, value);
}
return array;
}
/**
* 数组指定位置后面添加元素
* @description 如果数组为空,则在0位置添加元素
* @param {Array} array 数组
* @param {Number} index 下标位置,默认0
* @param {*} value 添加的元素
* @returns {Array} 返回操作后的数组
*/
function arrayInsertAfter(array = [], index = 0, value = undefined) {
if (index < 0) return array;
if (array.length === 0) {
array.push(value);
} else {
if (index > array.length - 1) {
return array;
}
array.splice(index, 0, array.splice(index, 1, value)[0]);
}
return array;
}
/**
* 数组指定位置删除元素
* @param {Array} array 数组
* @param {Number} index 下标位置,默认0
* @returns {Array} 返回操作后的数组
*/
function arrayRemove(array = [], index = 0) {
if (index < 0 || index > array.length - 1) return array;
array.splice(index, 1);
return array;
}
/**
* 数组指定位置前面删除元素
* @param {Array} array 数组
* @param {Number} index 下标位置,默认0
* @returns {Array} 返回操作后的数组
*/
function arrayRemoveBefore(array = [], index = 0) {
if (index <= 0 || index > array.length - 1) return array;
array.splice(index - 1, 1);
return array;
}
/**
* 数组指定位置后面删除元素
* @param {Array} array 数组
* @param {Number} index 下标位置,默认0
* @returns {Array} 返回操作后的数组
*/
function arrayRemoveAfter(array = [], index = 0) {
if (index < 0 || index > array.length - 1) return array;
array.splice(index + 1, 1);
return array;
}
/**
* 数组置顶
* @param {Array} array 数组
* @param {Number} index 下标位置,默认0
* @returns {Array} 返回操作后的数组
*/
function arrayTop(array = [], index = 0) {
if (index < 0 || index > array.length - 1) return array;
array.unshift(array.splice(index, 1)[0]);
return array;
}
/**
* 数组置尾
* @param {Array} array 数组
* @param {Number} index 下标位置,默认0
* @returns {Array} 返回操作后的数组
*/
function arrayBottom(array = [], index = 0) {
if (index < 0 || index > array.length - 1) return array;
array.push(array.splice(index, 1)[0]);
return array;
}
/**
* 数组向上移动
* @param {Array} array 数组
* @param {Number} index 下标位置,默认0
* @returns {Array} 返回操作后的数组
*/
function arrayUp(array = [], index = 0) {
if (index < 0 || index > array.length - 1) return array;
if (index > 0) {
array.splice(index - 1, 0, array.splice(index, 1)[0]);
} else {
array.push(array.splice(index, 1)[0]);
}
return array;
}
/**
* 数组向下移动
* @param {Array} array 数组
* @param {Number} index 下标位置,默认0
* @returns {Array} 返回操作后的数组
*/
function arrayDown(array = [], index = 0) {
if (index < 0 || index > array.length - 1) return array;
if (index < array.length - 1) {
array.splice(index + 1, 0, array.splice(index, 1)[0]);
} else {
array.unshift(array.splice(index, 1)[0]);
}
return array;
}
/**
* 数组交换元素
* @param {Array} array 数组
* @param {Number} sourceIndex 原索引
* @param {Number} targetIndex 目标索引
* @returns {Array} 返回操作后的数组
*/
function arraySwap(array, sourceIndex, targetIndex) {
if (sourceIndex < 0 || targetIndex < 0 || sourceIndex > array.length - 1 || targetIndex > array.length - 1) {
return array;
}
[array[targetIndex], array[sourceIndex]] = [array[sourceIndex], array[targetIndex]];
return array;
}
/**
* 数组排序
* @param {Array} array 数组
* @param {Number} mode 排序模式,参考常量集合中 数组常量,默认是升序
* @returns {Array} 返回操作后的数组
*/
function arraySort(array, mode = SORT.ASC) {
return array.sort((a, b) => {
switch (mode) {
// 升序
case SORT.ASC:
return a - b;
// 降序
case SORT.DESC:
return b - a;
// 随机
case SORT.RANDOM:
return Math.random() - 0.5;
// 默认
default:
return array;
}
});
}
/**
* 数组属性混合排序
* @description 排序默认为asc升序
* @param {Array} array 数组
* @param {Array} props 排序的属性
* @returns {Array} 返回操作后的数组
*/
function arraySortBy(array, props) {
return array.sort((a, b) => {
for (let item of props) {
// 排序配置
let prop = "",
order = "asc";
if (isObject(item)) {
prop = item.prop;
order = item.order || "asc";
} else {
prop = item;
order = "asc";
}
// 排序逻辑
if (a[prop] < b[prop]) {
return order === "asc" ? -1 : 1;
}
if (a[prop] > b[prop]) {
return order === "asc" ? 1 : -1;
}
}
return 0;
});
}
/**
* 数组元素去重
* @param {Array} array 数组
* @returns {Array} 返回操作后的数组
*/
function arrayUnique(array) {
if (isEmpty(array)) return [];
return Array.from(new Set(array));
}
/**
* 数组打乱元素
* @description 可以适用于一些抽奖人员列表打乱顺序
* @param {Array} array 数组
* @returns {Array} 返回操作后的数组
*/
function arrayShuffle(array) {
for (let i = 1; i < array.length; i++) {
const random = Math.floor(Math.random() * (i + 1));
[array[random], array[i]] = [array[i], array[random]];
}
return array;
}
/* 数组转换 */
/**
* 普通数组转树形结构
* @description 包含id和pid属性关系的一维数组,转为children的树形结构
* @param {Array} array 数组
* @param {Object} setting 配置项
* @returns {Array} 返回树形节点
*/
function arrayToTree(array, setting = { key: "id", parentKey: "pid", childrenKey: "children" }) {
let key = setting.key,
parentKey = setting.parentKey,
childrenKey = setting.childrenKey;
// 数组或者key是否为空
if (!array || array.length === 0 || !key || key === "") return [];
// 获得子节点方法
const nodeChildren = function (node, childrenKey, newChildren) {
if (!node) {
return null;
}
if (typeof newChildren !== "undefined") {
node[childrenKey] = newChildren;
}
return node[childrenKey];
};
// 声明变量
let result = [];
let tempMap = {};
for (let i = 0; i < array.length; i++) {
// 如果源数据数组中有children,则需要删除掉,否则会导致和之前的children合并
array[i][childrenKey] && delete array[i][childrenKey];
tempMap[array[i][key]] = array[i];
}
for (let i = 0; i < array.length; i++) {
let parent = tempMap[array[i][parentKey]];
if (parent && array[i][key] !== array[i][parentKey]) {
let children = nodeChildren(parent, childrenKey);
if (!children) {
children = nodeChildren(parent, childrenKey, []);
}
children.push(array[i]);
} else {
result.push(array[i]);
}
}
// 返回结果
return result;
}
/**
* 树形结构转普通数组
* @param {Array} nodes 树形节点
* @param {Object} setting 配置项
* @returns {Array} 返回普通数组
*/
function treeToArray(nodes, setting = { childrenKey: "children" }) {
let childrenKey = setting.childrenKey;
let result = [];
for (let node of nodes) {
// 删除掉多余空的children
if (node[childrenKey] && !node[childrenKey].length) {
delete node[childrenKey];
}
result.push(node);
// 继续执行
if (node[childrenKey] && node[childrenKey].length) {
let array = treeToArray(node[childrenKey], setting);
array && result.push(...array);
}
}
// 返回结果
return result;
}
/* 数组求并集,交集,差集等 */
/**
* 数组求并集
* @description 数组1 和 数组2 合并一起的元素集合
* @param {Array} array1 数组1
* @param {Array} array2 数组2
* @returns {Array} 返回数组
*/
function arrayUnion(array1, array2) {
return [...new Set(array1.concat(array2))];
}
/**
* 数组求交集
* @description 数组1 和 数组2 相同的元素集合
* @param {Array} array1 数组1
* @param {Array} array2 数组2
* @returns {Array} 返回数组
*/
function arrayIntersect(array1, array2) {
return [...new Set(array1)].filter((item) => array2.includes(item));
}
/**
* 数组求差集
* @description 数组1 中不包含 数组2 的元素集合
* @param {Array} array1 数组1
* @param {Array} array2 数组2
* @returns {Array} 返回数组
*/
function arrayDifference(array1, array2) {
return [...new Set(array1)].filter((item) => !array2.includes(item));
}
/**
* 数组求补集
* @description 数组1 和 数组2 不相同的元素集合
* @param {Array} array1 数组1
* @param {Array} array2 数组2
* @returns {Array} 返回数组
*/
function arrayComplement(array1, array2) {
return [
...[...new Set(array1)].filter((item) => !array2.includes(item)),
...[...new Set(array2)].filter((item) => !array1.includes(item)),
];
}
/* 对象转换 */
/**
* map转object
* @param {Map} map 参数
* @returns {Object} 返回Object
*/
function mapToObject(map) {
let obj = Object.create(null);
for (let [k, v] of map) {
obj[k] = v;
}
return obj;
}
/**
* map转json字符串
* @param {Map} map 参数
* @returns {String} 返回Json字符串
*/
function mapToJson(map) {
return JSON.stringify(mapToObject(map));
}
/**
* object转map
* @param {Object} obj 参数
* @returns {Map} 返回Map
*/
function objectToMap(obj) {
let map = new Map();
for (let k of Object.keys(obj)) {
map.set(k, obj[k]);
}
return map;
}
/**
* json字符串转map
* @param {String} json json字符串
* @returns {Map} 返回Map
*/
function jsonToMap(json) {
return objectToMap(JSON.parse(json));
}
/**
* json对象转json字符串
* @param {Object} json json对象
* @returns {String} 返回Json字符串
*/
function stringifyJson(json) {
return JSON.stringify(json);
}
/**
* json字符串转json对象
* @param {String} json json字符串
* @returns {Object} 返回Json对象
*/
function parseJson(json) {
if (isEmpty(json)) return;
return JSON.parse(json);
}
/* 数据拷贝,对比,合并等操作 */
/**
* 浅拷贝数据
* @param {*} source 拷贝的数据
* @returns {*} 返回浅拷贝的数据
*/
function clone(source) {
return Object.assign(source);
}
/**
* 深拷贝数据
* @param {*} source 拷贝的数据
* @returns {*} 返回深拷贝的数据
*/
function deepClone(source) {
// Object
if (isObject(source)) {
let copy = {};
for (let attr in source) {
if (source.hasOwnProperty(attr)) copy[attr] = deepClone(source[attr]);
}
return copy;
}
// Array
else if (isArray$4(source)) {
let copy = [];
for (let i = 0, len = source.length; i < len; i++) {
copy[i] = deepClone(source[i]);
}
return copy;
}
// Date
else if (isDate(source)) {
let copy = new Date();
copy.setTime(source.getTime());
return copy;
}
// Other 原路返回源数据
else {
return source;
}
}
/**
* 比较两个对象是否相等
* @description 方法只能对比简单的对象,不能包含function,另外对象的属性顺序不一致也是相等的
* @param {Object} obj1 对象1
* @param {Object} obj2 对象2
* @returns {Boolean} 返回true和false
*/
function objectEquals(obj1, obj2) {
// 比较值相等
if (obj1 === obj2) {
return true;
}
// 比较Date
if (obj1 instanceof Date && obj2 instanceof Date) {
return obj1.getTime() === obj2.getTime();
}
// 对象比较引用
if (!obj1 || !obj2 || (typeof obj1 !== "object" && typeof obj2 !== "object")) {
return obj1 === obj2;
}
// 比较原型
if (obj1.prototype !== obj2.prototype) {
return false;
}
// 比较对象的值
const keys = Object.keys(obj1);
if (keys.length !== Object.keys(obj2).length) {
return false;
} else {
return keys.every((k) => objectEquals(obj1[k], obj2[k]));
}
}
/**
* 合并对象
* @param {Object} target 目标对象
* @param {Object[]} source 原对象列表
* @returns {Object} 返回合并后的对象
*/
function merge(target, ...source) {
return Object.assign(target, ...source);
}
/* 根据字符串属性路径操作目标对象 */
/**
* 根据字符串属性路径获取目标对象的值
* @example
* let res = {code:200, data:{rows:[], pages:{current:1,pageSize:20}}}
* this._getValueByPath(res, 'data.pages.pageSize'); // 这里会输出20
* @param {Object} target 目标对象
* @param {String} path 字符串属性路径
* @returns {Object} 返回目标对象
*/
function getValueByPath(target, path = "data") {
const paths = (path || "data").split(".");
let current = target;
for (const index in paths) {
let part = paths[index];
// 判断属性是取的数组
let { propName, propIndex, isArray } = _getTargetPathPart(paths[index]);
if (isArray) {
// 获取的数组下标超出实际的长度
if (propIndex < 0 || propIndex >= current[propName].length) {
return undefined;
}
// 逐层向下找到对应属性的值
current = current[propName][propIndex];
}
// 判断属性是取的对象属性
else {
// 如果属性不存在,则返回空的
if (!current || !current.hasOwnProperty(part) || typeof current !== "object") {
return undefined;
}
// 逐层向下找到对应属性的值
current = current[part];
}
}
return current;
}
/**
* 根据字符串属性路径设置目标对象的值
* @example
* let res = {code:200, data:{rows:[], pages:{current:1,pageSize:20}}}
* this._setValueByPath(res, 'data.pages.pageSize', 30); // 打印res对象会发现pageSize的值改为了30
* @param {Object} target 目标对象
* @param {String} path 字符串属性路径
* @param {*} value 值
*/
function setValueByPath(target, path = "data", value) {
const paths = (path || "data").split(".");
let current = target;
// 遍历到路径的倒数第二个属性
for (let index = 0; index < paths.length - 1; index++) {
let part = paths[index];
let nextPart = paths[index + 1];
// 判断属性是取的数组
let { propName, propIndex, isArray } = _getTargetPathPart(part);
if (isArray) {
// 检查当前属性是否存在,若不存在或者不是数组,则创建一个数组
if (!current || !current.hasOwnProperty(propName)) {
current[propName] = [];
}
// 确保数组长度足够容纳指定索引
while (current[propName].length <= propIndex) {
current[propName].push({});
}
// 逐层向下找到对应属性的值
current = current[propName][propIndex];
} else {
// 如果不是数组索引形式,处理普通对象属性,如果是.1.数组形式,则还是返回数组
if (!current || !current.hasOwnProperty(part) || typeof current[part] !== "object") {
current[part] = isNaN(nextPart) ? [] : {};
}
// 移动到对象的下一层属性
current = current[part];
}
}
// 处理最后一个路径部分
let lastPath = paths[paths.length - 1];
let { propName, propIndex, isArray } = _getTargetPathPart(lastPath);
if (isArray) {
// 检查当前属性是否存在,若不存在或者不是数组,则创建一个数组
if (!current.hasOwnProperty(propName) || !Array.isArray(current[propName])) {
current[propName] = [];
}
// 确保数组长度足够容纳指定索引
while (current[propName].length <= propIndex) {
current[propName].push({});
}
// 逐层向下找到对应属性的值
current[propName][propIndex] = value;
} else {
current[lastPath] = value;
}
// 返回源对象
return target;
}
/* 内部方法 */
/**
* 获得目标路径的片段
* @param path
* @returns {{propName: (*|undefined), propIndex: (*|number|undefined), isArray: boolean}}
*/
function _getTargetPathPart(path) {
let pathArrayMatch = path.match(/^(\w+)\[(\d+)]$/);
return {
propName: (pathArrayMatch && pathArrayMatch[1]) || undefined, // 属性名
propIndex: pathArrayMatch ? parseInt(pathArrayMatch[2], 10) : undefined, // 属性数组时候的下标
isArray: !!pathArrayMatch, // 是否是数组
};
}
/**
* 防抖函数
* @description 事件执行后,在延迟时间内如果再次执行,会清空定时器重新延迟执行,举例:用户在输入框进行输入搜索,最终是会获取到最后一次输入,节约请求资源
* @param {Function} fn 目标函数
* @param {Number} delay 延迟时间,单位毫秒,默认 1*1000 毫秒
* @param {Boolean} immediate 是否立即执行,默认true
* @returns {Function} 返回function()
*/
function debounce(fn, delay = 1 * 1000, immediate = true) {
let timer;
return function () {
const _args = arguments;
// 先关闭定时器
if (timer) clearTimeout(timer);
// 立即执行判断
if (immediate) {
// 如果需要立即执行
// 开启新定时器防止短时间内再次触发
const canExecute = !timer;
timer = setTimeout(function () {
timer = null;
}, delay);
if (canExecute) fn.apply(this, _args);
} else {
// 如果不需要立即执行
// 每次触发开启新定时器即可
timer = setTimeout(function () {
fn.apply(this, _args);
}, delay);
}
};
}
/**
* 节流函数
* @description 高频触发时,在指定时间间隔内只执行一次,举例:监听页面滚动,不会频繁触发,只会在固定时间内获取一次
* @param {Function} fn 目标函数
* @param {Number} interval 时间间隔,单位毫秒,默认1*1000毫秒
* @returns {Function} 返回function()
*/
function throttle(fn, interval = 1 * 1000) {
let timer;
return function () {
const _args = arguments;
// 有定时器则返回
if (timer) return;
timer = setTimeout(() => {
timer = null;
fn.apply(this, _args);
}, interval);
};
}
/**
* 睡眠延迟执行
* @description 需要配合 async/await 来达到延迟效果
* @param {Number} delay 延迟时间,单位毫秒,默认1*1000毫秒
*/
function sleep(delay = 1 * 1000) {
return new Promise((resolve) => setTimeout(resolve, delay));
}
/* 快捷日期 */
/**
* 今天
*@returns {String} 返回日期字符串
*/
function today() {
return getDate();
}
/**
* 昨天
* @returns {String} 返回日期字符串
*/
function yesterday() {
return formatDate(addDate(new Date(), -1));
}
/**
* 明天
*@returns {String} 返回日期字符串
*/
function tomorrow() {
return formatDate(addDate(new Date(), 1));
}
/**
* 上周(7天前日期)
* @param {Date} date 日期参数,默认当前日期
* @returns {String} 返回日期字符串
*/
function lastWeek(date = new Date()) {
return formatDate(addDate(date, -7));
}
/**
* 下周(7天后日期)
* @param {Date} date 日期参数,默认当前日期
* @returns {String} 返回日期字符串
*/
function nextWeek(date = new Date()) {
return formatDate(addDate(date, 7));
}
/**
* 上个月(30天前日期)
* @param {Date} date 日期参数,默认当前日期
* @returns {String} 返回日期字符串
*/
function lastMonth(date = new Date()) {
return formatDate(addDate(date, -30));
}
/**
* 下个月(30天后日期)
* @param {Date} date 日期参数,默认当前日期
* @returns {String} 返回日期字符串
*/
function nextMonth(date = new Date()) {
return formatDate(addDate(date, 30));
}
/**
* 上一年(365天前日期)
* @param {Date} date 日期参数,默认当前日期
* @returns {String} 返回日期字符串
*/
function lastYear(date = new Date()) {
return formatDate(addDate(date, -365));
}
/**
* 下一年(365天后日期)
* @param {Date} date 日期参数,默认当前日期
* @returns {String} 返回日期字符串
*/
function nextYear(date = new Date()) {
return formatDate(addDate(date, 365));
}
/* 判断当前日期 */
/**
* 是否为上午
* @param {Date} date 日期参数,默认当前日期
* @returns {Boolean} 返回结果
*/
function isAM(date = new Date()) {
return date.getHours() < 12;
}
/**
* 是否为下午
* @param {Date} date 日期参数,默认当前日期
* @returns {Boolean} 返回结果
*/
function isPM(date = new Date()) {
return date.getHours() >= 12;
}
/**
* 是否为今天
* @param {Date} date 日期参数,默认当前日期
* @returns {Boolean} 返回结果
*/
function isToday(date = new Date()) {
// 此刻日期
let nowDate = new Date();
// 判断日期
return ["getFullYear", "getMonth", "getDate"].every((i) => nowDate[i]() === date[i]());
}
/**
* 是否为昨天
* @param {Date} date 日期参数,默认当前日期
* @returns {Boolean} 返回结果
*/
function isYesterday(date = new Date()) {
// 计算时间差
let startTime = date.getTime();
let nowTime = Date.now();
let diffTime = nowTime - startTime;
let diffDay = parseInt$1(diffTime / (1000 * 60 * 60 * 24));
return diffDay === 1;
}
/**
* 是否为前天
* @param {Date} date 日期参数,默认当前日期
* @returns {Boolean} 返回结果
*/
function isBeforeYesterday(date = new Date()) {
// 计算时间差
let startTime = date.getTime();
let nowTime = Date.now();
let diffTime = nowTime - startTime;
let diffDay = parseInt$1(diffTime / (1000 * 60 * 60 * 24));
return diffDay === 2;
}
/**
* 是否为明天
* @param {Date} date 日期参数,默认当前日期
* @returns {Boolean} 返回结果
*/
function isTomorrow(date = new Date()) {
// 计算时间差
let startTime = date.getTime();
let nowTime = Date.now();
let diffTime = startTime - nowTime;
let diffDay = parseInt$1(diffTime / (1000 * 60 * 60 * 24));
return diffDay === 1;
}
/**
* 是否为后天
* @param {Date} date 日期参数,默认当前日期
* @returns {Boolean} 返回结果
*/
function isAfterTomorrow(date = new Date()) {
// 计算时间差
let startTime = date.getTime();
let nowTime = Date.now();
let time = startTime - nowTime;
let diffDay = parseInt$1(time / (1000 * 60 * 60 * 24));
return diffDay === 2;
}
/**
* 是否为工作日
* @param {Date} date 日期参数,默认当前日期
* @returns {Boolean} 返回结果
*/
function isWorkday(date = new Date()) {
let dayOfWeek = getDayOfWeek(date