一、进阶工具类方法:
/* eslint-disable no-irregular-whitespace */
// 算法
// 获取数据类型
const getType = (obj) => {
const class2type = {
'[object Boolean]': 'boolean',
'[object Number]': 'number',
'[object String]': 'string',
'[object Function]': 'function',
'[object Array]': 'array',
'[object Date]': 'date',
'[object RegExp]': 'regexp',
'[object Object]': 'object',
'[object Error]': 'error',
'[object Symbol]': 'symbol'
}
if (obj == null) {
return obj + ''
}
// javascript高级程序设计中提供了一种方法,可以通用的来判断原始数据类型和引用数据类型
const str = Object.prototype.toString.call(obj)
console.log('str', str)
console.log('typeof obj', typeof obj)
return typeof obj === 'object' || typeof obj === 'function' ? class2type[str] || 'object' : typeof obj
}
// 拆解URL参数中queryString
const getUrlQuery = (queryStr) => {
//const [, query] = queryStr.split('?')
// 去除hash标识
const [query,] = queryStr.split('?')[1].split('#')
if (query) {
return query.split('&').reduce((pre, cur) => {
const [key, val] = cur.split('=')
pre[key] = decodeURIComponent(val)
return pre
}, {})
}
return {}
}
// 字符串相加
/**
* @param {string} num1
* @param {string} num2
* @return {string}
*/
const addStrings = function (num1, num2) {
if (num1 === num2 && num1 === "0") {
return num1
}
// 切割成数组倒过来,倒过来是因为可能存在长度不一的字符串
num1 = num1.split("").reverse();
num2 = num2.split("").reverse();
const len = Math.max(num1.length, num2.length);
let flag = 0;
const result = [];
for (let i = 0; i < len; i++) {
const n1 = +num1[i] || 0;
const n2 = +num2[i] || 0;
let sum = n1 + n2 + flag;
flag = 0;
// 进1
if (sum > 9) {
sum -= 10
flag = 1
}
result.push(sum);
}
// 仍存在进1标志,手动进1
if (flag) {
result.push(flag)
}
return result.reverse().join("");
}
// 翻转字符串里的单词
const reverseWords = function (s) {
return s.trim().split(/\s+/).reverse().join(' ');
};
// 二分查找
const binarySearch = function (nums, target) {
let left = 0, right = nums.length - 1;
while (left <= right) {
const mid = Math.floor((right - left) / 2) + left;
const num = nums[mid];
if (num === target) {
return mid;
} else if (num > target) {
right = mid - 1;
} else {
left = mid + 1;
}
}
return -1;
}
// 两个数组的交集
const intersection = function (nums1, nums2) {
const set_intersection = (set1, set2) => {
if (set1.size > set2.size) {
return set_intersection(set2, set1);
}
const intersection = new Set();
for (const num of set1) {
if (set2.has(num)) {
intersection.add(num);
}
}
return [...intersection];
}
const set1 = new Set(nums1);
const set2 = new Set(nums2);
return set_intersection(set1, set2);
};
// 两数之和下标
const twoSumIndex = (nums, target) => {
const prevNums = {}; // 存储出现过的数字,和对应的索引
for (let i = 0; i < nums.length; i++) { // 遍历元素
const curNum = nums[i]; // 当前元素
const targetNum = target - curNum; // 满足要求的目标元素
const targetNumIndex = prevNums[targetNum]; // 在prevNums中获取目标元素的索引
if (targetNumIndex !== undefined) { // 如果存在,直接返回 [目标元素的索引,当前索引]
return [targetNumIndex, i];
} else { // 如果不存在,说明之前没出现过目标元素
prevNums[curNum] = i; // 存入当前的元素和对应的索引
}
}
}
// 基本计算器(字符串)
const calculate = function (s) {
const ops = [1];
let sign = 1;
let ret = 0;
const n = s.length;
let i = 0;
while (i < n) {
if (s[i] === ' ') {
i++;
} else if (s[i] === '+') {
sign = ops[ops.length - 1];
i++;
} else if (s[i] === '-') {
sign = -ops[ops.length - 1];
i++;
} else if (s[i] === '(') {
ops.push(sign);
i++;
} else if (s[i] === ')') {
ops.pop();
i++;
} else {
let num = 0;
while (i < n && !(isNaN(Number(s[i]))) && s[i] !== ' ') {
num = num * 10 + s[i].charCodeAt() - '0'.charCodeAt();
i++;
}
ret += sign * num;
}
}
return ret;
}
// 验证IP地址
const validIPAddress = (queryIP) => {
return queryIP.match(/^((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)($|(?!\.$)\.)){4}$/) ? "IPv4" :
queryIP.match(/^(([\da-fA-F]{1,4})($|(?!:$):)){8}$/) ? "IPv6" : "Neither";
}
// 过滤 JSON 中的无效条目
const filterByID = (item) => {
if (Number.isFinite(item.id) && item.id !== 0) {
return true;
}
return false;
}
// 判断某个字符串长度(要求支持表情)
const unicodeLength = (str) => {
return Array.from(str).length
}
// 循环递归深拷贝
const deepClone = (obj, hash = new WeakMap()) => {
if (obj === null) return obj; // 如果是null或者undefined我就不进行拷贝操作
if (obj instanceof Date) return new Date(obj);
if (obj instanceof RegExp) return new RegExp(obj);
// 可能是对象或者普通的值 如果是函数的话是不需要深拷贝
if (typeof obj !== "object") return obj;
// 是对象的话就要进行深拷贝
if (hash.get(obj)) return hash.get(obj);
let cloneObj = new obj.constructor();
// 找到的是所属类原型上的constructor,而原型上的 constructor指向的是当前类本身
hash.set(obj, cloneObj);
for (let key in obj) {
// if (obj.hasOwnProperty(key)) {
if (Object.prototype.hasOwnProperty.call(obj, key)) {
// 实现一个递归拷贝
cloneObj[key] = deepClone(obj[key], hash);
}
}
return cloneObj;
}
// 判断页面是通过PC端还是移动端访问
const isMobile = () => {
if (/Mobi|Android|iPhone/i.test(navigator.userAgent)) {
// 当前设备是移动设备
return true
}
return false
}
// 获取全局对象
const getGlobal = () => {
if (typeof self !== 'undefined') {
return self
}
if (typeof window !== 'undefined') {
return window
}
if (typeof global !== 'undefined') {
return global
}
throw new Error('无法找到全局对象')
}
// 归并排序
const mergeSort = (arr) => {
let array = mergeSortRec(arr)
return array
}
// 若分裂后的两个数组长度不为 1,则继续分裂
// 直到分裂后的数组长度都为 1,
// 然后合并小数组
const mergeSortRec = (arr) => {
let lenth = arr.length
if (lenth == 1) {
return arr
}
let mid = Math.floor(lenth / 2)
let left = arr.slice(0, mid)
let right = arr.slice(mid, lenth)
return merge(mergeSortRec(left), mergeSortRec(right))
}
const merge = (left, right) => {
let result = [],
ileft = 0,
iright = 0
while (ileft < left.length && iright < right.length) {
if (left[ileft] < right[iright]) {
// eslint-disable-next-line no-irregular-whitespace
result.push(left[ileft++])
} else {
result.push(right[iright++])
}
}
while (ileft < left.length) {
result.push(left[ileft++])
}
while (iright < right.length) {
result.push(right[iright++])
}
return result
}
// 数组扁平化、去重、排序
const newArray = (arr) => {
while (arr.some(Array.isArray)) {
arr = [].concat(...arr)
}
arr = [...new Set(arr)].sort((a, b) => a - b)
return arr
}
// add方法
const add = (...args) => {
const _add = (...args1) => {
return add(...args, ...args1)
}
_add.value = () => args.reduce((t, e) => t + e)
return _add
}
// 快速排序算法
const sortArray = (nums) => {
quickSort(0, nums.length - 1, nums);
return nums;
}
const quickSort = (start, end, arr) => {
if (start < end) {
const mid = sort(start, end, arr);
quickSort(start, mid - 1, arr);
quickSort(mid + 1, end, arr);
}
}
const sort = (start, end, arr) => {
const base = arr[start];
let left = start;
let right = end;
while (left !== right) {
while (arr[right] >= base && right > left) {
right--;
}
arr[left] = arr[right];
while (arr[left] <= base && right > left) {
left++;
}
arr[right] = arr[left];
}
arr[left] = base;
return left;
}
// 带并发的异步调度器 Scheduler
// 页面引用 const scheduler = new Scheduler();
class Scheduler {
constructor() {
this.waitTasks = []; // 待执行的任务队列
this.excutingTasks = []; // 正在执行的任务队列
this.maxExcutingNum = 2; // 允许同时运行的任务数量
}
add(promiseMaker) {
if (this.excutingTasks.length < this.maxExcutingNum) {
this.run(promiseMaker);
} else {
this.waitTasks.push(promiseMaker);
}
}
run(promiseMaker) {
const len = this.excutingTasks.push(promiseMaker);
const index = len - 1;
promiseMaker().then(() => {
this.excutingTasks.splice(index, 1);
if (this.waitTasks.length > 0) {
this.run(this.waitTasks.shift());
}
});
}
}
// 浮点数保留两位小数
// 将浮点数四舍五入,取小数点后2位
// 注⚠️,数据类型不变。
const toDecimal = (x) => {
var f = parseFloat(x);
if (isNaN(f)) {
return;
}
f = Math.round(x * 100) / 100;
return f;
}
// 强制保留2位小数,如:2,会在2后面补上00.即2.00
// 注意⚠️,数据类型变为字符串类型。
const toDecimal2 = (x) => {
var f = parseFloat(x);
if (isNaN(f)) {
return false;
}
var f = Math.round(x * 100) / 100;
var s = f.toString();
var rs = s.indexOf('.');
if (rs < 0) {
rs = s.length;
s += '.';
}
while (s.length <= rs + 2) {
s += '0';
}
return s;
}
// 保留两位小数 浮点数四舍五入 位数不够 不补0
// 注意⚠️,数据类型不变。
const fomatFloat = (src, pos) => {
return Math.round(src * Math.pow(10, pos)) / Math.pow(10, pos);
}
//加法函数,用来得到精确的加法结果
//说明:javascript的加法结果会有误差,在两个浮点数相加的时候会比较明显。这个函数返回较为精确的加法结果。
//调用:accAdd(arg1,arg2)
//返回值:arg1加上arg2的精确结果
const accAdd = (arg1, arg2) => {
var r1, r2, m;
try { r1 = arg1.toString().split(".")[1].length } catch (e) { r1 = 0 }
try { r2 = arg2.toString().split(".")[1].length } catch (e) { r2 = 0 }
m = Math.pow(10, Math.max(r1, r2))
return (arg1 * m + arg2 * m) / m
}
//给Number类型增加一个add方法,调用起来更加方便。
Number.prototype.add = function (arg) {
return accAdd(arg, this);
}
//减法函数,用来得到精确的减法结果
//说明:javascript的加法结果会有误差,在两个浮点数相加的时候会比较明显。这个函数返回较为精确的减法结果。
//调用:accSub(arg1,arg2)
//返回值:arg1减去arg2的精确结果
const accSub = (arg1, arg2) => {
var r1, r2, m, n;
try { r1 = arg1.toString().split(".")[1].length } catch (e) { r1 = 0 }
try { r2 = arg2.toString().split(".")[1].length } catch (e) { r2 = 0 }
m = Math.pow(10, Math.max(r1, r2));
//last modify by deeka
//动态控制精度长度
n = (r1 >= r2) ? r1 : r2;
return ((arg1 * m - arg2 * m) / m).toFixed(n);
}
//除法函数,用来得到精确的除法结果
//说明:javascript的除法结果会有误差,在两个浮点数相除的时候会比较明显。这个函数返回较为精确的除法结果。
//调用:accDiv(arg1,arg2)
//返回值:arg1除以arg2的精确结果
const accDiv = (arg1, arg2) => {
var t1 = 0, t2 = 0, r1, r2;
try { t1 = arg1.toString().split(".")[1].length } catch (e) { }
try { t2 = arg2.toString().split(".")[1].length } catch (e) { }
while (Math) {
r1 = Number(arg1.toString().replace(".", ""))
r2 = Number(arg2.toString().replace(".", ""))
return (r1 / r2) * Math.pow(10, t2 - t1);
}
}
//给Number类型增加一个div方法,调用起来更加方便。
Number.prototype.div = function (arg) {
return accDiv(this, arg);
}
//乘法函数,用来得到精确的乘法结果
//说明:javascript的乘法结果会有误差,在两个浮点数相乘的时候会比较明显。这个函数返回较为精确的乘法结果。
//调用:accMul(arg1,arg2)
//返回值:arg1乘以arg2的精确结果
const accMul = (arg1, arg2) => {
var m = 0, s1 = arg1.toString(), s2 = arg2.toString();
try { m += s1.split(".")[1].length } catch (e) { }
try { m += s2.split(".")[1].length } catch (e) { }
return Number(s1.replace(".", "")) * Number(s2.replace(".", "")) / Math.pow(10, m)
}
//给Number类型增加一个mul方法,调用起来更加方便。
Number.prototype.mul = function (arg) {
return accMul(arg, this);
}
// obj是获取属性的对象,path是路径,fallback是默认值
const getAttributeObj = (obj, path, fallback) => {
const parts = path.split(".");
const key = parts.shift();
if (typeof obj[key] !== "undefined") {
return parts.length > 0 ?
getAttributeObj(obj[key], parts.join("."), fallback) :
obj[key];
}
// 如果没有找到key返回fallback
return fallback;
}
// 防止XSS攻击:对接口返回的数据进行过滤和转义处理
const escapeHtml = (unsafe) => {
return unsafe
.replace(/&/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/g, '"')
.replace(/'/g, ''');
}
// 截断数字
const toFixedNum = (n, fixed) => `${n}`.match(new RegExp(`^-?\d+(?:.\d{0,${fixed}})?`))[0]
//四舍五入
const roundNum = (n, decimals = 0) => Number(`${Math.round(`${n}e${decimals}`)}e-${decimals}`)
// 数字补零
const replenishZero = (num, len, zero = 0) => num.toString().padStart(len, zero)
// 字符串转对象
const strParse = (str) => JSON.parse(str.replace(/(\w+)\s*:/g, (_, p1) => `"${p1}":`).replace(/\'/g, "\""))
// uuid
const uuid = (a) => (a ? (a ^ ((Math.random() * 16) >> (a / 4))).toString(16) : ([1e7] + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, uuid))
uuid()
// 强制等待
const sleep = async (t) => new Promise((resolve) => setTimeout(resolve, t));
export {
getType,
getUrlQuery,
addStrings,
reverseWords,
binarySearch,
intersection,
twoSumIndex,
calculate,
validIPAddress,
filterByID,
unicodeLength,
deepClone,
isMobile,
getGlobal,
mergeSort,
newArray,
add,
sortArray,
Scheduler,
// 浮点数保留两位小数
toDecimal,
toDecimal2,
fomatFloat,
// 浮点数加减乘除
accAdd,
accSub,
accMul,
accDiv,
// 递归获取对象属性
getAttributeObj,
escapeHtml,
toFixedNum,
roundNum,
replenishZero,
strParse,
uuid,
sleep,
}