每天学习10个实用JavaScript代码片段,加深对 JavaScript 语法的理解,积累代码优化经验,新的内容来了,学习阅读代码是提高编码技能的最佳方式。如果觉得内容能够带来点帮助,可以查看专栏《碎片时间学习JavaScript代码》其他内容,又或者有用到的需求片段,不妨在留言区留言。
1. 时间格式化
JavaScript中时间相关的脚本库有很多,如 Moment 和 Luxon,提供比较丰富的时间相关的方法,这里介绍原生方法的实现。
const formatDate = (date, formatStr = "YYYY-MM-DD") => {
date = new Date(date);
const padStart = (str) => str.toString().padStart(2, "0");
const month = date.getMonth() + 1;
const year = date.getFullYear();
const day = date.getDate();
const hours = date.getHours();
const minutes = date.getMinutes();
const seconds = date.getSeconds();
return formatStr
.replace(/\bYYYY\b/g, year)
.replace(/\bYY\b/g, `${year}`.substring(2))
.replace(/\bMM\b/g, padStart(month))
.replace(/\bM\b/g, month)
.replace(/\bDD\b/g, padStart(day))
.replace(/\bD\b/g, day)
.replace(/\bHH\b/g, padStart(hours))
.replace(/\bhh\b/g, hours)
.replace(/\bmm\b/g, padStart(minutes))
.replace(/\bss\b/g, padStart(seconds));
};
console.log(formatDate(Date.now())); // 2021-09-28
console.log(formatDate(Date.now(), "YYYY-MM-DD HH:mm:ss")); // 2021-09-28 18:49:29
复制代码
2. 求数组差集
求两个数组的差集,可以通过数据结构 Set
来实现,具体实现请参阅《JavaScript中的Set数据操作:交集、差集、交集、对称差集》。这里介绍另一种方式,从另一个数组中减去一个数组并返回差值,并保留结果列表中其元素的顺序。
const difference = (a, b) => a.filter((item) => b.indexOf(item) < 0);
const arrA = [1, 2, 3, 4];
const arrB = [3, 4, 5, 6];
console.log(difference(arrA, arrB)); // // [ 1, 2 ]
复制代码
3. 深度检索
代码片段利用了数组 reduce
方法,检索深度嵌套对象、数组和Map中的深度值,可以访问属于对象本身或其原型的值,例如访问字符串长度或获取Map
的大小。
const getDeepValue = (dict, path) => {
path = Array.isArray(path) ? path : path.split(".");
const value = path.reduce(
(obj, key) =>
obj === undefined
? null
: obj instanceof Map
? obj.get(key) ?? obj[key]
: obj[key],
dict
);
return value === undefined ? null : value;
};
const articlePage = {
list: [
{
title: "js",
keywords: ["js", "reduce"],
},
],
};
// 下面展示相当于 articlePage.list[0].keywords[1]
console.log(getDeepValue(articlePage, "list.0.keywords.1")); // reduce
复制代码
4. 对象数组排序
对于对象数组或者多维书序,可能需要对某个 key
或者索引进行排序,这在项目中是比较常见的需求。
const sortByKey = (obj, key) => {
const values =
obj instanceof Map ? Array.from(obj.values()) : Object.values(obj);
return values.reduce(
(keyedObj, value) => ({ ...keyedObj, [value[key]]: value }),
{}
);
};
const arrayObjs = [
{ id: 2, title: "二" },
{ id: 1, title: "一" },
{ id: 3, title: "三" },
];
console.log(sortByKey(arrayObjs, "id"));
/*
{
'1': { id: 1, title: '一' },
'2': { id: 2, title: '二' },
'3': { id: 3, title: '三' }
}
*/
const arrays = [
[1, 1, 4],
[1, 1, 1],
];
console.log(sortByKey(arrays, "2")); // { '1': [ 1, 1, 1 ], '4': [ 1, 1, 4 ] }
复制代码
5. 合并两个对象
代码片段实现合并两个对象,但不处理深嵌套,保持原来结构,通过使用递归的方法深度合并对象和数组,返回一个新的副本。
const mergeObjects = (a, b) => {
if (a === null || typeof a !== "object") return b ?? {};
if (b === null || typeof b !== "object") return b ?? {};
const obj = Array.isArray(a) ? [...a] : a;
for (const key in b) {
if (b.hasOwnProperty(key)) {
obj[key] = mergeObjects(obj[key], b[key]);
}
}
return obj;
};
const obj1 = {
title: "abc",
remark: {
text: "remark",
},
};
const obj2 = {
title: "bcd",
desc: {
info: "test",
},
};
console.log(mergeObjects(obj1, obj2)); // { title: 'bcd', remark: { text: 'remark' }, desc: { info: 'test' } }
复制代码
6. 类私有域
JavaScript 有自己的方法来创建类私有成员,但目前还处于ES2020试验草案中,并且语法比较奇怪,以 #
作为前缀。下面代码片段使用闭包、作用域来实现类的私有域。
const Helper = (() => {
const defaultValue = (val, defVal) => {
if (val && val !== "") {
return val;
} else {
return defVal;
}
};
const apiEndpoint = "/Auth";
// 对外暴露的类
class Utility {
constructor() {
this.loginPath = `${apiEndpoint}/login`;
}
getVal(key, defVal) {
return defaultValue(key, defVal);
}
}
return Utility;
})();
const testHelper = new Helper();
console.log(testHelper.getVal(undefined, 0)); // 0
console.log(testHelper.loginPath); // /Auth/login
复制代码
7. 求最短长度
代码片段将找到给定字符串中单词的最短长度,可以从一些函数中获得灵感,例如 split()
、sort()
和 pop()
。
const findShortLength = (str) =>
str
.split(" ")
.sort((a, b) => b.length - a.length)
.pop().length;
const testString = "A lazy person will find an easy way to do it.";
const testString2 = "Finding the best WebGL tool.";
console.log(findShortLength(testString)); // 1
console.log(findShortLength(testString2)); // 3
复制代码
8. 数据分组
对于一组数据通过特定 key 的值来分组,使用 reduce
方法对数据进行分组,并按照分组进行归类。
const groupBy = (obj, key) => {
const values =
obj instanceof Map || obj instanceof Set
? Array.from(obj.values())
: Object.values(obj);
return values.reduce((acc, value) => {
const groupKey = value[key];
if (!Array.isArray(acc[groupKey])) {
acc[groupKey] = [value];
} else {
acc[groupKey].push(value);
}
return acc;
}, {});
};
const arrayBooks = [
{ title: "战国策", category: "历史" },
{ title: "秦汉史", category: "历史" },
{ title: "秦始皇", category: "历史" },
{ title: "刘伯温", category: "人物传记" },
{ title: "张居正", category: "人物传记" },
];
console.log(groupBy(arrayBooks, "category"));
/*
{
'历史': [
{ title: '战国策', category: '历史' },
{ title: '秦汉史', category: '历史' },
{ title: '秦始皇', category: '历史' }
],
'人物传记': [
{ title: '刘伯温', category: '人物传记' },
{ title: '张居正', category: '人物传记' }
]
}
*/
复制代码
9. 一次性事件
代码片段实现监听DOM事件,并对绑定的事件只执行一次。
const onceTrigger = (callback) => {
const handler = (e) => {
e.currentTarget.removeEventListener(e.type, handler);
callback(e);
};
return handler;
};
const elemSubmit = document.querySelector("button");
elemSubmit.addEventListener(
"click",
onceTrigger(() => {
console.log("click");
})
);
复制代码
10. 条件事件
和上面的一次性事件类似,条件事件意味着当满足一定条件后事件将不再响应,如按钮点击三次后将不再出发 click
事件。
const conditionTrigger = (callback, checkFn) => {
const handler = (e) => {
if (checkFn(e)) {
e.currentTarget.removeEventListener(e.type, handler);
}
callback(e);
};
return handler;
};
let clickCount = 0;
const elemSubmit = document.querySelector("button");
elemSubmit.addEventListener(
"click",
conditionTrigger(
() => {
console.log("click");
clickCount = clickCount + 1;
},
() => clickCount > 3
)
);
复制代码
总结
JavaScript 的灵活性使得编程充满技巧,作为 JavaScript 开发人员,要感觉自己就是一个魔术师,合理灵活的利用其技巧。