一些优雅的代码

这里用来收藏一下看到的优雅的、惊为天人的代码

json array 过滤指定的属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// 原始值
let data = [
{
"id": "1234",
"key1": "val1",
"key2": "val2",
"name": "someone",
"age": 39
},
{
"id": "1234",
"key1": "val1",
"key2": "val2",
"name": "someone",
"age": 39
}
]

// 期望得到
[
{
"id": "1234",
"name": "someone",
"age": 39
},
{
"id": "1234",
"name": "someone",
"age": 39
}
]

// 代码
data.map(({ key1, key2, ...other }) => other)

删除v站包含特定关键词的主题

1
document.querySelectorAll(".cell.item").forEach(node => node.textContent.includes("王伟") && node.remove())

获取数组中的最大值

1
2
let values = [1, 2, 5, 7, 1, 6, 8];
console.log(Math.max(...values)) // 8

数组中的值求和

1
2
const arr = [1, 2, 3, 4, 5]
let sum = arr.reduce((prev, current, index, arr) => prev + current); // 15

js精度丢失问题

计算机存储双精度浮点数需要先把十进制数转换为二进制的科学记数法的形式,然后计算机以自己的规则{符号位+(指数位+指数偏移量的二进制)+小数部分}存储二进制的科学记数法,因为存储时有位数限制(64位),并且某些十进制的浮点数在转换为二进制数时会出现无限循环,会造成二进制的舍入操作(0舍1入),当再转换为十进制时就造成了计算误差。

参考资料

1
2
3
4
5
6
7
// 加法运算
function add(num1, num2) {
const num1Digits = (num1.toString().split('.')[1] || '').length
const num2Digits = (num2.toString().split('.')[1] || '').length
const baseNum = Math.pow(10, Math.max(num1Digits, num2Digits))
return (num1 * baseNum + num2 * baseNum) / baseNum
}
加减乘除运算
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
const operationObj = {
/**
* 处理传入的参数,不管传入的是数组还是以逗号分隔的参数都处理为数组
* @param args
* @returns {*}
*/
getParam(args) {
return Array.prototype.concat.apply([], args);
},

/**
* 获取每个数的乘数因子,根据小数位数计算
* 1.首先判断是否有小数点,如果没有,则返回1;
* 2.有小数点时,将小数位数的长度作为Math.pow()函数的参数进行计算
* 例如2的乘数因子为1,2.01的乘数因子为100
* @param x
* @returns {number}
*/
multiplier(x) {
let parts = x.toString().split('.');
return parts.length < 2 ? 1 : Math.pow(10, parts[1].length);
},

/**
* 获取多个数据中最大的乘数因子
* 例如1.3的乘数因子为10,2.13的乘数因子为100
* 则1.3和2.13的最大乘数因子为100
* @returns {*}
*/
correctionFactor() {
let args = Array.prototype.slice.call(arguments);
let argArr = this.getParam(args);
return argArr.reduce((accum, next) => {
let num = this.multiplier(next);
return Math.max(accum, num);
}, 1);
},

/**
* 加法运算
* @param args
* @returns {number}
*/
add(...args) {
let calArr = this.getParam(args);
// 获取参与运算值的最大乘数因子
let corrFactor = this.correctionFactor(calArr);
let sum = calArr.reduce((accum, curr) => {
// 将浮点数乘以最大乘数因子,转换为整数参与运算
return accum + Math.round(curr * corrFactor);
}, 0);
// 除以最大乘数因子
return sum / corrFactor;
},

/**
* 减法运算
* @param args
* @returns {number}
*/
subtract(...args) {
let calArr = this.getParam(args);
let corrFactor = this.correctionFactor(calArr);
let diff = calArr.reduce((accum, curr, curIndex) => {
// reduce()函数在未传入初始值时,curIndex从1开始,第一位参与运算的值需要
// 乘以最大乘数因子
if (curIndex === 1) {
return Math.round(accum * corrFactor) - Math.round(curr * corrFactor);
}
// accum作为上一次运算的结果,就无须再乘以最大因子
return Math.round(accum) - Math.round(curr * corrFactor);
});
// 除以最大乘数因子
return diff / corrFactor;
},

/**
* 乘法运算
* @param args
* @returns {*}
*/
multiply(...args) {
let calArr = this.getParam(args);
let corrFactor = this.correctionFactor(calArr);
calArr = calArr.map((item) => {
// 乘以最大乘数因子
return item * corrFactor;
});
let multi = calArr.reduce((accum, curr) => {
return Math.round(accum) * Math.round(curr);
}, 1);
// 除以最大乘数因子
return multi / Math.pow(corrFactor, calArr.length);
},

/**
* 除法运算
* @param args
* @returns {*}
*/
divide(...args) {
let calArr = this.getParam(args);
let quotient = calArr.reduce((accum, curr) => {
let corrFactor = this.correctionFactor(accum, curr);
// 同时转换为整数参与运算
return Math.round(accum * corrFactor) / Math.round(curr * corrFactor);
});
return quotient;
}
};

console.log(operationObj.add(0.1, 0.7)); // 0.8
console.log(operationObj.subtract(0.3, 0.2)); // 0.1
console.log(operationObj.multiply(0.7, 180)); // 126
console.log(operationObj.divide(0.3, 0.1)); // 3

统计页面一共有多少种HTML标签

1
new Set([...document.querySelectorAll('*')].map(n => n.nodeName)).size

批量给对象中的value设为空

1
2
3
4
5
6
7
8
9
const obj = {
"holiday": "true",
"name": "元旦",
"wage": "3",
"date": "2022-01-01",
"rest": "85"
}

Object.keys(obj).forEach(item => obj[item] = '')

格式化时间

是的,几乎算是每个前端写的最多的代码了。但是最近看到微信小程序demo里面的代码,感慨其优雅。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
const formatTime = (date) => {
const year = date.getFullYear()
const month = date.getMonth() + 1
const day = date.getDate()
const hour = date.getHours()
const minute = date.getMinutes()
const second = date.getSeconds()

return `${[year, month, day].map(formatNumber).join("-")} ${[
hour,
minute,
second,
]
.map(formatNumber)
.join(":")}`
}

const formatNumber = (n) => {
n = n.toString()
return n[1] ? n : `0${n}`
}

// 2022-11-14 21:39:01
console.log((formatTime(new Date())));

可拓展的格式化时间

预先定义一个对象,key为可能的正则表达式,即y、M、d、H、m、s、q(季度)、S(毫秒)等,value为每个正则表达式对应的实际值。
预先匹配年份,确定年份的值。然后遍历对象,确定可能有的月份、天、时、分和秒。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
Date.prototype.format = function (fmt) {
var o = {
"M+": this.getMonth() + 1, //月份
"d+": this.getDate(), //日
"H+": this.getHours(), //小时
"m+": this.getMinutes(), //分
"s+": this.getSeconds(), //秒
"q+": Math.floor((this.getMonth() + 3) / 3), //季度
"S": this.getMilliseconds() //毫秒
};
if (/(y+)/.test(fmt)) {
// RegExp.$1是RegExp的一个属性,指的是与正则表达式匹配的第一个子匹配(以括号为标志)字符串。类推RegExp.$2是第二个匹配对象
fmt = fmt.replace(RegExp.$1, (this.getFullYear() + "").substr(4 -
RegExp.$1.length));
}
for (var k in o) {
if (new RegExp("(" + k + ")").test(fmt)) {
fmt = fmt.replace(RegExp.$1, (RegExp.$1.length == 1) ? (o[k]) :
(("00" + o[k]).substr(("" + o[k]).length)));
}
}
return fmt;
};

var d = new Date();
console.log(d.format('yyyy-MM-dd HH:mm:ss.S')); // 2017-11-26 14:46:13.894
console.log(d.format('yyyy-MM-dd')); // 2017-11-26
console.log(d.format('yyyy-MM-dd q HH:mm:ss')); // 2017-11-26 4 14:46:13

比较日期大小

如果是时间戳则直接比较数字大小

1
2
3
4
5
6
7
8
function CompareDate(dateStr1, dateStr2) {
var date1 = dateStr1.replace(/-/g, "\/");
var date2 = dateStr2.replace(/-/g, "\/");
return new Date(date1) > new Date(date2);
}

CompareDate("2018-07-30 7:31", "2018-07-31 7:30"); // false
CompareDate("2018-08-01 17:31", "2018-08-01 17:30"); // true

还有另外一种办法,回顾一下为什么可以用数字和对象做比较呢?JavaScript会自动做类型转换,对象的话会调用valueOf()函数转换为时间戳

1
2
3
4
5
6
7
8
9
setInterval(function () {
if (Date.now() >= new Date("2016-09-12 15:59:00")) {
$("#seckillQuantity").val(1); //1盒
$(".buyButtons.J_buyButtons").click(); //抢购按钮
var value = $(".answerList").children().eq(0).html(); //验证码取值
$("#randomAnswer").val(value); //验证码填值
$(".answer-button").children().eq(0).click(); //提交验证码
}
}, 10);

计算当前日期前后N天的日期

假如知道一个日期为2018-08-01,需要求出该时期前、后3天的日期。前3天日期为2018-07-29,后3天日期为2018-08-04。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
function GetDateStr(AddDayCount) {
var dd = new Date();
dd.setDate(dd.getDate() + AddDayCount); //获取AddDayCount天后的日期
var y = dd.getFullYear();
//获取当前月份的日期,不足10补0
var m = (dd.getMonth() + 1) < 10 ? "0" + (dd.getMonth() + 1) : (dd.getMonth() + 1);
var d = dd.getDate() < 10 ? "0" + dd.getDate() : dd.getDate(); //获取当前几号,
//不足10补0
return y + "-" + m + "-" + d;
}

console.log("半年前:" + GetDateStr(-180)); // 半年前:2018-02-02
console.log("三月前:" + GetDateStr(-90)); // 三月前:2018-05-03
console.log("一月前:" + GetDateStr(-30)); // 一月前:2018-07-02
console.log("昨天:" + GetDateStr(-1)); // 昨天:2018-07-31
console.log("今天:" + GetDateStr(0)); // 今天:2018-08-01
console.log("明天:" + GetDateStr(1)); // 明天:2018-08-02
console.log("后天:" + GetDateStr(2)); // 后天:2018-08-03
console.log("一月后:" + GetDateStr(30)); // 一月后:2018-08-31
console.log("三月后:" + GetDateStr(90)); // 三月后:2018-10-30
console.log("半年后:" + GetDateStr(180)); // 半年后:2019-01-28

计算两个日期的时间差

设计的规则是向下取整法。大于1天,不满2天的按照1天处理;大于1小时,不满2小时的按照1小时处理。

计算两个日期的时间差的主要思路如下。
· 将传入的时间字符串中的“-”分隔符转换为“/”。
· 将转换后的字符串构造成新的Date对象。
· 以毫秒作为最小的处理单位,然后根据处理维度,进行相应的描述计算。例如天换算成毫秒,就为“1000 * 3600 * 24”。
· 两个时间都换算成秒后,进行减法运算,与维度值相除即可得到两个时间的差值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
function GetDateDiff(startTime, endTime, diffType) {
// 将yyyy-MM-dd的时间格式转换为yyyy/MM/dd的时间格式
startTime = startTime.replace(/\-/g, "/");
endTime = endTime.replace(/\-/g, "/");
// 将计算间隔类性字符转换为小写
diffType = diffType.toLowerCase();
var sTime = new Date(startTime); // 开始时间
var eTime = new Date(endTime); // 结束时间
//作为除数的数字
var divNum = 1;
switch (diffType) {
case "second":
divNum = 1000;
break;
case "minute":
divNum = 1000 * 60;
break;
case "hour":
divNum = 1000 * 3600;
break;
case "day":
divNum = 1000 * 3600 * 24;
break;
default:
break;
}
return parseInt((eTime.getTime() - sTime.getTime()) / parseInt(divNum));
}

var result1 = GetDateDiff("2018-07-30 18:12:34", '2018-08-01 9:17:30', "day");
var result2 = GetDateDiff("2018-07-29 20:56:34", '2018-08-01 9:17:30', "hour");
console.log("两者时间差为:" + result1 + "天。");
console.log("两者时间差为:" + result2 + "小时。");

获取字符串中所有的数字,并进行求和

由于平常使用小米笔记记账,全是文本类型,月底汇总时手动相加显得太蠢,就写了一段代码来实现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 提取字符串中的数字
function getNumFormStr(str) {
const result = str.replace(/[\u4e00-\u9fa5\s]/g, ' ')
return result
}

// 将字符串中的数字转成数字数组,并排序
function str2NumArray(str) {
const numStr = getNumFormStr(str)
const res = numStr.match(/\d+(\.\d+)?/g).map(Number).sort((a, b) => b - a);
return res
}

// 求和
const sum = str2NumArray(str).reduce((prev, current, index, arr) => prev + current); // 15
console.log(sum);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 极为先进的chatGpt给的代码
function extracNumberAndCalcSum(str) {
const regex = /(\S+)\s+([\d.]+)/g;
const matches = str.matchAll(regex);
const res = [];
let sum = 0;
for (const match of matches) {
const word = match[1];
const num = parseFloat(match[2]);
if (!isNaN(num)) {
res.push({ word, num });
sum += num
}
}
return { res, sum }
}

扁平数据结构转Tree

实际开发中经常遇到的问题,将后端返回的数据转成tree结构。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
// 例如接口返回
const list = [
{ id: 1, name: '部门1', parentId: 0 },
{ id: 2, name: '部门2', parentId: 1 },
{ id: 3, name: '部门3', parentId: 1 },
{ id: 4, name: '部门4', parentId: 3 },
{ id: 5, name: '部门5', parentId: 4 },
];

// 期望
[
{
"id": 1,
"name": "部门1",
"pid": 0,
"children": [
{
"id": 2,
"name": "部门2",
"pid": 1,
"children": []
},
{
"id": 3,
"name": "部门3",
"pid": 1,
"children": [
// 结果 ,,,
]
}
]
}
]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
const convert = (arr) => {
const result = [];
const map = new Map();
arr.forEach(item => {
map.set(item.id, item);
});
arr.forEach(item => {
const parent = map.get(item.parentId);
if (parent) {
if (parent.children) {
parent.children.push(item);
} else {
parent.children = [item];
}
} else {
result.push(item);
}
});
return result;
};

一些优雅的代码
https://xypecho.github.io/2021/01/26/一些优雅的代码/
作者
很青的青蛙
发布于
2021年1月26日
许可协议