前端 JS 规范
前端不参与金额计算
-
前端由于 JS 天生的计算精度问题会导致计算结果不复合预期。因此,所有数值计算类全部交由服务端开发统一计算,前端只做透传,不做任何计算
-
金额前端保存,必须使用 string 类型,前端请求服务端传参,涉及到金额字段必须使用 string 传递(禁止使用 number 类型)以此避免 number 类型的值
可能会在表示数值区间外数据溢出
还有当数值过大默认转为科学计算法显示
的问题。
不使用双等号进行判断
JavaScript 中使用两等号会因为 隐式类型转换 (opens in a new tab) 而导致结果和预期不符,进而导致程序 bug,例如:
"10" == 10; // true
null == undefined; // true
"" == 0; // true
true == 1; // true
false == 0; // true
- 判断是否相等请使用
===
,请勿使用==
- 判断是否不相等请使用
!==
,请勿使用!=
危险的内置函数需要 catch
一些内置函数对不当的参数会抛错,导致功能不可用,严重甚至白屏:
- JSON.parse 当接收一个不能被反序列化的字符串时会抛错
JSON.parse("{sum: 123},");
// Uncaught SyntaxError: Expected property name or '}' in JSON at position 1
// ✅ good case
// 将 JSON.parse 封装成通用的 jsonParseSafely
export function jsonParseSafely<T>(str: string, defaultValue: any = {}): T {
try {
return JSON.parse(str);
} catch (error) {
console.warn("JSON.parse", str, "failed", error);
return defaultValue;
}
}
// 在业务代码中使用 jsonParseSafely 而非 JSON.parse
const jsonData = jsonParseSafely(data.jsonData || "[]");
- decodeURIComponent 如果 encodedURI 包含一个 % 后面不跟随两个十六进制数字,或者转义序列没有编码有效的 UTF-8 字符,则会抛出 URIError (opens in a new tab) 异常
decodeURIComponent("%="); // Uncaught URIError: URI malformed
- JSON.stringify 当被序列化对象存在 循环引用 或者 BigInt 类型属性时,会抛出 TypeError (opens in a new tab)
// JSON.stringify 处理循环引用
var obj1 = {};
var obj2 = {};
obj1.prop = obj2;
obj2.prop = obj1;
JSON.stringify(obj1); // Uncaught TypeError: Converting circular structure to JSON
// JSON.stringify 处理 BigInt
var obj = {
num: BigInt(123456),
};
JSON.stringify(obj); // Uncaught TypeError: Do not know how to serialize a BigInt
避免直接访问嵌套属性
访问层级过深的属性容易引起空指针,导致页面白屏等严重错误。
- 优先推荐 TS 可选级联语法,即 Optional Chaining (opens in a new tab):props?.maybeObj?.a,但是请勿滥用,滥用的坏处在于
- 在本来该进行错误处理的地方,用这种方式绕过
- 本来应该能监控发现问题的地方,被这种方式掩盖掉
- 让 undefined 继续传递下去,引发更深层次的问题
- 若变量缺少类型定义则使用 lodash.get (opens in a new tab)
业务场景下禁止使用客户端时间
- 客户端时间可被用户随意更改,任何与日期时间相关的业务逻辑都必须采用服务端下发的时间
禁止使用 JS 保留字 和 浏览器全局变量 作为变量和模块名
- 使用 JS 保留字 和 浏览器全局变量 作为变量和模块名可能导致不可预知的问题
避免在 this 上随意添加属性
- 在 this 上随意挂载属性可能会覆盖内置属性导致不可预期的运行时错误,如果必须加则应当使用
$
或_
前缀