公共
获取数据类型
function getType(val) {
const lowerCaseType = Function.prototype.call.bind(Object.prototype.toString);
return lowerCaseType(val).replace(/\[object (\w+)\]/, "$1").toLowerCase()
}
移出事件监听
document.getElementById("button").addEventListener("click", () => {
console.log("clicked!");
});
// 方法一 - removeEventListener
document.getElementById("button").removeEventListener("click", () => {
console.log("clicked!");
});
// 方法一优化:
const cb = () => {
console.log("clicked!");
};
document.getElementById("button").addEventListener("click", cb);
document.getElementById("button").removeEventListener("click", cb);
// 方法二 - 设置只会调用一次
document.getElementById("button").addEventListener(
"click",
() => {
console.log("clicked!");
},
{ once: true }
);
// 方法三 - AbortController
const button = document.getElementById("button");
const ct = new AbortController();
const { signal } = ct;
button.addEventListener("click", () => console.log("clicked!"), { signal });
window.addEventListener("resize", () => console.log("resized!"), { signal });
document.addEventListener("keyup", () => console.log("pressed!"), { signal });
// Remove all listeners at once:
controller.abort();
数字
金额格式化
// 数字格式化 3000 -> 3,000
function formatNumber(str) {
return ("" + str).replace(/\B(?=(\d{3})+(?!\d))/g, ",");
// number [number].toLocaleString('en-US')
}
// {number} number:要格式化的数字
// {number} decimals:保留几位小数
// {string} dec_point:小数点符号
// {string} thousands_sep:千分位符号
const moneyFormat = (number, decimals, dec_point, thousands_sep) => {
number = (number + "").replace(/[^0-9+-Ee.]/g, "");
const n = !isFinite(+number) ? 0 : +number;
const pre = !isFinite(+decimals) ? 2 : Math.abs(decimals);
const sep = typeof thousands_sep === "undefined" ? "," : thousands_sep;
const dec = typeof dec_point === "undefined" ? "." : dec_point;
let s = "";
const toFixedFix = function (n, pre) {
const k = Math.pow(10, pre);
return "" + Math.ceil(n * k) / k;
};
s = (pre ? toFixedFix(n, pre) : "" + Math.round(n)).split(".");
const re = /(-?\d+)(\d{3})/;
while (re.test(s[0])) {
s[0] = s[0].replace(re, "$1" + sep + "$2");
}
if ((s[1] || "").length < pre) {
s[1] = s[1] || "";
s[1] += new Array(pre - s[1].length + 1).join("0");
}
return s.join(dec);
};
对象
深拷贝
需要用到 utils.getType 函数。
function cloneDeep(obj) {
// 维护两个储存循环引用的数组
const parents = []
const children = []
const _clone = obj => {
if (obj === null) return null;
const parentType = getType(obj);
if (parentType !== 'object') return obj
let child, proto;
if (parentType === 'array') {
// 对数组做特殊处理
child = []
} else if (parentType === 'regexp') {
// 对正则对象做特殊处理
child = new RegExp(obj.source, getRegExp(obj))
if (obj.lastIndex) child.lastIndex = obj.lastIndex
} else if (parentType === 'date') {
// 对Date对象做特殊处理
child = new Date(obj.getTime())
} else {
// 处理对象原型
proto = Object.getPrototypeOf(obj)
// 利用Object.create切断原型链
child = Object.create(proto)
}
// 处理循环引用
const index = parents.indexOf(obj)
if (index !== -1) {
// 如果父数组存在本对象,说明之前已经被引用过,直接返回此对象
return children[index]
}
parents.push(obj)
children.push(child)
for (const i in obj) {
// 递归
child[i] = _clone(obj[i])
}
return child
}
return _clone(obj)
}
比较对象是否相同
通常我们可以直接使用 lodash.isEqual 方法。
function deepCompare(x, y) {
let i, l, leftChain, rightChain;
function compare2Objects(x, y) {
let p;
if (isNaN(x) && isNaN(y) && typeof x === 'number' && typeof y === 'number') {
return true;
}
if (x === y) {
return true;
}
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();
}
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;
}
if (leftChain.indexOf(x) > -1 || rightChain.indexOf(y) > -1) {
return false;
}
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;
}
for (i = 1, l = arguments.length; i < l; i++) {
leftChain = [];
rightChain = [];
if (!compare2Objects(arguments[0], arguments[i])) {
return false;
}
}
return true;
}
数组
乱序
function shuffle(arr) {
let i = arr.length;
while (i) {
let j = Math.floor(Math.random() * i--);
[arr[i], arr[j]] = [arr[j], arr[i]]
}
return arr
}
获取最后一项
function getLstMember(arr) {
// return arr.slice(-1)[0]
return arr.at(-1)
}
获取交集
需要对数组项进行比较,用到了 obj.deepCompare。
// 基础类型
function getIntersection(left, right) {
return left.filter(val => right.indexOf(val) > -1)
}
// 对象
function getIntersectionComplex(arr1, arr2) {
return arr2.filter(v => arr1.some(n => deepCompare(n, v)))
}
去重
// 基础类型
function unique(arr) {
const obj = {};
return arr.filter(ele => {
if (!obj[ele]) {
obj[ele] = true;
return true;
}
})
}
// 根据字段
function uniqueByKey(arr, key) {
const res = new Map();
return arr.filter((item) => !res.has(item[key]) && res.set(item[key], 1));
}
获取并集
// 基础类型
function getUnion(left, right) {
return left.concat(right.filter(v => !left.includes(v)))
}
获取差集
// 数组差集 数组arr1相对于arr2所没有的
function getDiff(arr1, arr2) {
return arr1.filter(item => !new Set(arr2).has(item))
}
// 数组差集 object[]
function getDiffObjectArray(arr1, arr2) {
return arr1.filter(function (v) {
return arr2.every(n => JSON.stringify(n) !== JSON.stringify(v))
})
}
获取补集
// 基础类型
function getComplement(left, right) {
return Array.from(new Set(left.concat(right).filter(v => !new Set(left).has(v) || !new Set(right).has(v))))
}
// 对象
function getComplementComplex(arr1, arr2) {
let arr3 = arr1.concat(arr2);
return arr3.filter(function (v) {
return arr1.every(n => JSON.stringify(n) !== JSON.stringify(v)) || arr2.every(n => JSON.stringify(n) !== JSON.stringify(v))
})
}
计算百分比
常用与饼状图等
// 计算合计
function calcObjArrTotal(arr, key = 'value') {
return arr.reduce((pre, next) => {
return pre + next[key]
}, 0)
}
// 计算百分比
function fixObjArrRate(arr, key = 'value') {
let max = 0, maxIdx = 0;
const calcFloat = num => Math.floor(num * 100) / 100
const total = calcObjArrTotal(arr, key = 'value');
const scraps = 100 - arr.reduce((pre, next, index) => {
if (arr[index].value > max) {
max = arr[index].value;
maxIdx = index
}
arr[index].rate = calcFloat(next[key] * 100 / total);
return pre + arr[index].rate
}, 0)
arr[maxIdx].rate = + (scraps + arr[maxIdx].rate).toFixed(2)
return arr
}
字符串
对象与字符串
// 对象转url
function stringifyUrl(search = {}) {
return Object.entries(search)
.reduce((t, v) => `${t}${v[0]}=${encodeURIComponent(v[1])}&`, Object.keys(search).length ? "?" : "")
.replace(/&$/, "");
}
// 字符串转对象
function getUrlParams(url) {
const paramsStr = /.+\?(.+)$/.exec(url)[1]; // 将 ? 后面的字符串取出来
const paramsArr = paramsStr.split('&'); // 将字符串以 & 分割后存到数组中
let paramsObj = {};
// 将 params 存到对象中
paramsArr.forEach(param => {
if (/=/.test(param)) { // 处理有 value 的参数
let [key, val] = param.split('='); // 分割 key 和 value
val = decodeURIComponent(val); // 解码
val = /^\d+$/.test(val) ? parseFloat(val) : val; // 判断是否转为数字
if (paramsObj.hasOwnProperty(key)) { // 如果对象有 key,则添加一个值
paramsObj[key] = [].concat(paramsObj[key], val);
} else { // 如果对象没有这个 key,创建 key 并设置值
paramsObj[key] = val;
}
} else { // 处理没有 value 的参数
paramsObj[param] = true;
}
})
return paramsObj;
}
// 获取地址栏参数
function getParamByUrl(key){
const url = new URL(location.href);
return url.searchParams.get(key);
};
脱敏
// 手机号脱敏
function hideMobile(mobile) {
return mobile.replace(/^(\d{3})\d{4}(\d{4})$/, "$1****$2")
}
其他
下载文件
function download(url, fileName) {
const x = new window.XMLHttpRequest()
x.open('GET', url, true)
x.responseType = 'blob'
x.onload = () => {
const url = window.URL.createObjectURL(x.response)
const a = document.createElement('a')
a.href = url
a.download = fileName
a.click()
}
x.send()
}
// downloadFile('/api/download', {id}, '文件名')
const downloadFile = (api, params, fileName, type = "get") => {
axios({
method: type,
url: api,
responseType: "blob",
params: params,
})
.then((res) => {
let str = res.headers["content-disposition"];
if (!res || !str) {
return;
}
let suffix = "";
// 截取文件名和文件类型
if (str.lastIndexOf(".")) {
fileName
? ""
: (fileName = decodeURI(
str.substring(str.indexOf("=") + 1, str.lastIndexOf("."))
));
suffix = str.substring(str.lastIndexOf("."), str.length);
}
// 如果支持微软的文件下载方式(ie10+浏览器)
if (window.navigator.msSaveBlob) {
try {
const blobObject = new Blob([res.data]);
window.navigator.msSaveBlob(blobObject, fileName + suffix);
} catch (e) {
console.log(e);
}
} else {
// 其他浏览器
let url = window.URL.createObjectURL(res.data);
let link = document.createElement("a");
link.style.display = "none";
link.href = url;
link.setAttribute("download", fileName + suffix);
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
window.URL.revokeObjectURL(link.href);
}
})
.catch((err) => {
console.log(err.message);
});
};
全屏
// 目标元素全屏
// launchFullScreen(document.getElementById('#app'))
function launchFullScreen(element) {
if (element.requestFullscreen) {
element.requestFullscreen();
} else if (element.mozRequestFullScreen) {
element.mozRequestFullScreen();
} else if (element.msRequestFullscreen) {
element.msRequestFullscreen();
} else if (element.webkitRequestFullscreen) {
element.webkitRequestFullScreen();
}
return false
}
// 退出全屏
function exitFullScreen() {
if (document.exitFullscreen) {
document.exitFullscreen();
} else if (document.msExitFullscreen) {
document.msExitFullscreen();
} else if (document.mozCancelFullScreen) {
document.mozCancelFullScreen();
} else if (document.webkitExitFullscreen) {
document.webkitExitFullscreen();
}
}
获取上一页url
function getLastPageUrl() {
return document.referrer;
}
获取指定父节点
function findParent(dom, className) {
let parent = dom.parentElement;
if (dom && parent.className !== className) {
parent = findParent(dom.parentElement, className);
}
return parent;
}
创建链接字符串
function createLinkStr(str, url) {
return str.link(url); // `<a href="www.google.com">google</a>`
}
复制、粘贴
// 复制
async function performCopy(event) {
event.preventDefault();
if (
navigator.clipboard &&
navigator.clipboard.read &&
navigator.clipboard.write
) {
try {
await navigator.clipboard.writeText(copyText);
console.log(`${copyText} copied to clipboard`);
} catch (err) {
console.error("Failed to copy: ", err);
}
}
}
// 粘贴
async function performPaste(event, target) {
event.preventDefault();
if (
!(
navigator.clipboard &&
navigator.clipboard.read &&
navigator.clipboard.write
)
) {
return false;
}
try {
const text = await navigator.clipboard.readText();
// 赋值
target.value = text
console.log("Pasted content: ", text);
} catch (err) {
console.error("Failed to read clipboard contents: ", err);
}
}
获取图片原始宽高
function getImageNatural(img, cb) {
if (img.naturalWidth) {
// 现代浏览器
nWidth = img.naturalWidth;
nHeight = img.naturalHeight;
cb({ w: nWidth, h: nHeight });
} else {
// IE6/7/8
var image = new Image();
image.src = img.attr("src");
if (image.complete) {
cb({ w: image.width, h: image.height });
} else {
image.onload = function () {
var w = image.width;
var h = image.height;
cb({ w: w, h: h });
};
}
}
}
LocalStorage 缓存
class CustomCache {
constructor(isLocal = true) {
this.storage = isLocal ? localStorage : sessionStorage;
}
setItem(key, value) {
if (typeof value === "object") value = JSON.stringify(value);
this.storage.setItem(key, value);
}
getItem(key) {
try {
return JSON.parse(this.storage.getItem(key));
} catch (err) {
return this.storage.getItem(key);
}
}
removeItem(key) {
this.storage.removeItem(key);
}
clear() {
this.storage.clear();
}
key(index) {
return this.storage.key(index);
}
length() {
return this.storage.length;
}
}
const localCache = new CustomCache();
const sessionCache = new CustomCache(false);