在项目开发过程中,会发现有一些代码反复需要,这些大部分是有第三方的脚本库来解决,有时不想为了一个功能安装一整个库,因此建议平时开发过程中将常用的函数封装成一个公共库。本文将带总结一下项目开发过程中可以用得到的函数。
1. 类型检查
JavaScript不是强类型语言,为了达到这个目的,最好的解决方案是 TypeScript,推荐在项目中使用 TypeScript,通常对于一些简单的类型检查,可以使用 typeof
。
typeof
的问题在于,如果将它用于原始类型和函数,它会很好用,但对于数组和对象,就不那么好用了,因为它们返回的都是 objects
。
const typeChecker = (() => { const type = Object.create(null); // null type.null = (x) => x === null; // undefined type.undefined = (x) => x === undefined; // nil null undefined type.nil = (x) => type.null(x) || type.undefined(x); // string type.string = (x) => !type.nil(x) && (typeof x === "string" || x instanceof String); // number type.number = (x) => !type.nil(x) && // NaN & Infinity have typeof "number" and this excludes that ((!isNaN(x) && isFinite(x) && typeof x === "number") || x instanceof Number); // boolean type.boolean = (x) => !type.nil(x) && (typeof x === "boolean" || x instanceof Boolean); // array type.array = (x) => !type.nil(x) && Array.isArray(x); // object. e.g: {}, new Object(), Object.create(null) type.object = (x) => ({}.toString.call(x) === "[object Object]"); // check for provided type instance type.type = (x, X) => !type.nil(x) && x instanceof X; // set type.set = (x) => type.type(x, Set); // map type.map = (x) => type.type(x, Map); // date type.date = (x) => type.type(x, Date); return type; })();
2. 创建类私有方法
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
3. 检查是否为空
有时只需要知道某些内容是否为空,并且根据要检查的内容,需要使用不同的方法,例如检查长度、大小或是否包含子元素。
下面代码片段封装了所有情况下的空值验证,包括 String
、Object
、Array
、Map
和 Set
。
const isEmpty = (x) => { if (Array.isArray(x) || typeof x === "string" || x instanceof String) { return x.length === 0; } if (x instanceof Map || x instanceof Set) { return x.size === 0; } if ({}.toString.call(x) === "[object Object]") { return Object.keys(x).length === 0; } return false; }; console.log(isEmpty(new Set())); // true console.log(isEmpty({})); // true
4. 获取集合最后一项
其他语言将此作为可以在数组上调用的方法或函数,但对于 JavaScript,需要自己编码实现这些工作,这里的集合包括 Array
、Set
、Map
。
const lastItem = (list) => { if (Array.isArray(list)) { return list.slice(-1)[0]; } if (list instanceof Set) { return Array.from(list).slice(-1)[0]; } if (list instanceof Map) { return Array.from(list.values()).slice(-1)[0]; } }; const testSet = new Set(); testSet.add(1); testSet.add(2); console.log(lastItem(testSet)); // 2
5. 随机数生成
随机数是一个常见的需求,生成指定范围的随机数。
const randomNumber = (max = 1, min = 0) => { if (min >= max) { [min, max] = [max, min]; } return Math.floor(Math.random() * (max - min) + min); }; console.log(randomNumber(1, 100));
6. 随机 ID 生成器
生成唯一 ID,之前有介绍过 nano 或者 UUID,这里介绍一种简单的方式,足够应对大部分的项目需求,以从当前时间(以毫秒为单位)或特定的整数和增量开始,也可以从字母数字字符生成ID。
// 创建从当前时间开始的唯一id(以毫秒为单位),每次请求时加1 const uniqueId = ((mil) => () => mil++)(Date.now()); // 创建指定长度的数字组成的唯一递增ID const uniqueIncrementingId = ((numb) => (length = 12) => `${numb++}`.padStart(length, "0"))(1); // 创建字母和数字组成的唯一 id const uniqueAlphaNumericId = (() => { const heyStack = "0123456789abcdefghijklmnopqrstuvwxyz"; const { length } = heyStack; const randomInt = () => Math.floor(Math.random() * length); return (length = 24) => Array.from({ length }, () => heyStack[randomInt()]).join(""); })(); console.log(uniqueId()); // 1633317380617 console.log(uniqueIncrementingId()); // 000000000001 console.log(uniqueIncrementingId()); // 000000000002 console.log(uniqueAlphaNumericId()); // e7r5y71cvfo18ccj1sgp70ts
7. 创建一系列数字
在 Python 中的有一个函数 range
,可以随机生成一系列数字,在 JavaScript 中需要自己编码实现。
const range = (maxOrStart, end = null, step = null) => { if (!end) { return Array.from({ length: maxOrStart }, (_, i) => i); } if (end <= maxOrStart) { return []; } if (step !== null) { return Array.from( { length: Math.ceil((end - maxOrStart) / step) }, (_, i) => i * step + maxOrStart ); } return Array.from( { length: Math.ceil(end - maxOrStart) }, (_, i) => i + maxOrStart ); }; console.log(range(10, 20, 3)); // [ 10, 13, 16, 19 ]
8. 格式化 JSON 字符串
在使用NodeJs将对象记录到控制台时经常使用 JSON.Stringify()
,其接受三个参数,语法如下:
JSON.stringify(value[, replacer [, space]])
value
:将要序列化成 一个 JSON 字符串的值。replacer
:可选,如果该参数是一个函数,则在序列化过程中,被序列化的值的每个属性都会经过该函数的转换和处理;如果该参数是一个数组,则只有包含在这个数组中的属性名才会被序列化到最终的 JSON 字符串中;如果该参数为 null 或者未提供,则对象所有的属性都会被序列化。space
:可选,指定缩进用的空白字符串,用于美化输出(pretty-print);如果参数是个数字,它代表有多少的空格;上限为10。该值若小于1,则意味着没有空格;如果该参数为字符串(当字符串长度超过10个字母,取其前10个字母),该字符串将被作为空格;如果该参数没有提供(或者为 null),将没有空格。
const stringify = (() => { const replacer = (key, val) => { if (typeof val === "symbol") { return val.toString(); } if (val instanceof Set) { return Array.from(val); } if (val instanceof Map) { return Array.from(val.entries()); } if (typeof val === "function") { return val.toString(); } return val; }; return (obj, spaces = 0) => JSON.stringify(obj, replacer, spaces); })(); const testSet = new Set(); testSet.add(1); testSet.add(2); const testMap = new Map(); testMap.set("title", "DevPoint"); const testObj = { title: "DevPoint", }; console.log(stringify(testSet)); // [1,2] console.log(stringify(testMap)); // [["title","DevPoint"]] console.log(stringify(testObj)); // {"title":"DevPoint"}
9. 函数链
使用过 jQuery 的开发者应该很清楚函数链,初次见到这种方式的时候感觉很酷,如将多个函数与相同的元素引用链接在一起,没有必要为每个函数调用多次附加元素引用。
const selectedDiv = $("#myDiv"); // 函数链方式 selectedDiv.css("color: red").height("100px").width("100px"); // 非函数链 selectedDiv.css("color: red"); selectedDiv.height("100px"); selectedDiv.width("100px");
一般在项目开发中的公共方法类可以考虑支持函数链,可以为其提供简洁的调用方式,下面代码展示了函数链的实现:
const Obj = { result: 0, addNumber: function (a, b) { this.result = a + b; return this; }, multiplyNumber: function (a) { this.result = this.result * a; return this; }, }; Obj.addNumber(10, 20).multiplyNumber(10); console.log(Obj.result); // 300
总结
定期总结项目开发中出现频率比较高的方法,去优化其是否为最佳,并为后期项目开发提供弹药。