持续更新...
多级解构
将部分变量从对象中解构出来的时候,涉及到多层对象的解构,可以直接一次完成,不需要分两次。
bad ❌
javascript
const { state, dispatch } = usePageReducer();
const { currStep } = state
console.log(currStep, 'currStep')
after good ✅
javascript
const { state: { currStep }, dispatch } = usePageReducer();
console.log(currStep, 'currStep')
// 当state为可选时可以加上默认值 {}
const { state: { currStep } = {}, dispatch } = usePageReducer();
includes 代替多个判断
在遇到多个条件相等判断时,可以用includes
代替多个||
,这样代码简洁,且其可以对NaN
进行相等比较。
bad ❌
js
if(x === 'undefined' || x === 'true' || (typeof x === 'number' && isNaN(x))) {
.....
}
after good ✅
js
const list = [false, '', true, false, undefined, null, NaN, {}, 0]
if(list.includes(x)) {
....
}
console.log(list.includes('')); // true
console.log(list.includes(false)); // true
console.log(list.includes(undefined)); // true
console.log(list.includes(null)); // true
console.log(list.includes(NaN)); // true
console.log(list.includes(0)); // true
console.log(list.includes({})); // false
if else换成三元运算
但是注意不要滥用三元运算符,过多的三元运算符嵌套不利于可读性性,所以遇到复杂的条件判断还是要考虑使用if/else、switch
拆分或者map
映射条件。
bad ❌
javascript
const testFunc = (a, b) => {
if(a > 0) {
return a
}
else{
return 0
}
}
after good ✅
javascript
const testFunc = (a, b) => {
return a > 0 ? a : 0
}
filter(Boolean)过滤
当想过滤所有转化为boolean
值后为false
的值时, 可以采用filter(Boolean)
简写的形式
bad ❌
js
const arr = [1, '1', '', 0, null, undefined, NaN, false]
const usefulArr = arr.filter(item => !!item) // [ 1, '1' ]
after good ✅
js
const arr = [1, '1', '', 0, null, undefined, NaN, false]
const usefulArr2 = arr.filter(Boolean) // [ 1, '1' ]
Object.is 比较一致
Object.is 相较于 === 的区别是其可以比对 -0和+0以及NaN间的比较结果,常用于方便的比较NaN
的情况
===
js
const checkSame = (value1, value2) => {
return value1 === value2
}
checkSame({}, {}) // false
checkSame('test', 'test') // true
checkSame(NaN, NaN) // false
checkSame(-0, +0) // true
js
const checkSame = (value1, value2) => {
return Object.is(value1, value2)
}
checkSame({}, {}) // false
checkSame('test', 'test') // true
checkSame(NaN, NaN) // true
checkSame(-0, +0) // false
return 无用条件
提前将后面不需要用到的判断条件从方法中return出去,这样可以减少判断层级和次数。
bad ❌
javascript
const testFunc = (a, b) => {
if(a && b === 1) {
return a + b
}
if(a && b === 2) {
return b
}
}
after good ✅
javascript
const testFunc = (a, b) => {
if(!a) return
if(b === 1) return a + b
if(b === 2) return b
}
合理使用可选链运算符
当可以确定数据对象的类型格式时,切忌不要过多的使用可选链运算符,防止不必要的判断
bad ❌
javascript
interface Response {
data: {
detail?: {
value: number
}
}
}
const res: Response = await getData()
const value = res?.data?.detail?.value
after good ✅
ts
interface Response {
data: {
detail?: {
value: number
}
}
}
const res: Response = await getData()
// 去掉了 data 的可选链运算取值
const value = res?.data.detail?.value
多次用到的变量提前解构
bad ❌
javascript
const num = obj.data + 10
return num > 0 ? num : obj.data * 10
after good ✅
javascript
const { data } = obj
const num = data + 10
return num > 0 ? num : data * 10
&& 使用要谨慎
我们往往会使用 &&
作为一个判断条件,但是不然如果前面的值有时是number
类型时,他可能并不会在条件不满足的时候返回false
,可以使用!!
进行转化成boolean
或者使用三元运算符
代替。
bad ❌
javascript
const arr = []
return arr.length && 'a'
after good ✅
javascript
const arr = []
return !!arr.length && 'a'
or
return arr.length ? 'a' : false
多用reduce
当我们遇到一些对象数组转化时,可以使用reduce很好的减少代码量,也可以让多次循环变成一次循环。
bad ❌
javascript
const disabledFieldData = ['enterpriseData#mytime#1', 'enterpriseData#mytime#2']
const func = (fatherKey: string) => {
const fieldKey = `enterpriseData#${fatherKey}#`;
// 执行了2次循环
return disabledFieldData.filter((item) => item.fieldKey.includes(fieldKey))
.map(item => Number(item.fieldKey.replace(fieldKey, '')));
}
func('mytime')
after good ✅
javascript
const disabledFieldData = ['enterpriseData#mytime#1', 'enterpriseData#mytime#2']
const func = (fatherKey: string) => {
const fieldKey = `enterpriseData#${fatherKey}#`;
// 一次循环搞定
return disabledFieldData.reduce((pre, cur) => {
if (cur.fieldKey.startsWith(fieldKey)) {
pre.push(Number(cur.fieldKey.replace(fieldKey, '')));
}
return pre;
}, [] as number[]);
}
func('mytime')
try...catch 多用finally
对于无论是 try还是catch都要执行的公共语法可以放在finally内。
bad ❌
javascript
let a = false
try {
const data = await api(...)
...
a = true
}.catch(err => {
console.log(err)
a = true
})
after good ✅
javascript
let a = false
try {
const data = await api(...)
...
}.catch(err => {
console.log(err)
}).finally(() => {
a = true
})
简写 ?? 和 ||
bad ❌
javascript
a = a ?? 'default'
b = b || 'default'
after good ✅
javascript
a ??= 'default'
b ||= 'default'
书写请求公共方法时要防止重复请求
以下两种错误方式第一种没有任何缓存可言,第二种在同一时间请求入参相同时依然无法避免重复发送请求
bad ❌
ts
// 获取菜鸟地址列表
funtion getStateCityList(key) {
return getStateCityListReq({ divisionId: key })
}
// or
let cacheValue = {}
async funtion getStateCityList(key) {
if(cacheValue[key]) return cacheValue
const res = await getStateCityListReq({ divisionId: key })
cacheValue[key] = res.data || []
return cacheValue[key]
}
采用缓存 Promise
的resolve
方法 或者 缓存整个return的Promise
可以实现防止重复请求
after good ✅
ts
// 菜鸟地址缓存
export const CacheAddressMap = new Map();
// 获取菜鸟地址列表(缓存 防止重复发送请求)
funtion getStateCityList(key) {
if (CacheAddressMap.has(key)) {
return CacheAddressMap.get(key)
}
const CacheKey = `${key}-pending`;
const pendingList = CacheAddressMap.get(CacheKey);
if (pendingList) {
return new Promise((resolve) => {
pendingList.push(resolve);
})
}
CacheAddressMap.set(CacheKey, []);
const paddingPromise = new Promise((resolve) => {
getStateCityListReq({ divisionId: key }).then((res) => {
const resData = res.data || [];
CacheAddressMap.set(key, resData);
CacheAddressMap.get(CacheKey)?.map((CacheResolve) => CacheResolve(resData));
CacheAddressMap.delete(CacheKey);
resolve(resData);
});
});
return paddingPromise
}
多类型条件收窄(TS)
当有一个变量有多种类型的可能性时我们往往习惯使用as
直接断言但是这是不可取的,可以使用is
方法类型收窄
bad ❌
ts
function test(response: Response1 || Response2){
if(response.type === 'test1') {
(response as Response1).list.forEach(item => {
.... // 类型 Response1 的处理
})
}else {
// 类型 Response2 的处理
}
}
after good ✅
ts
function checkResponseType(res: Response1 || Response2): res is Response1 {
if(response.type === 'test1') return true
return false
}
function test(response: Response1 || Response2){
if(checkResponseType(response)) {
response.list.forEach(item => {
.... // 类型 Response1 的处理
})
}else {
// 类型 Response2 的处理
}
}
如果一些属性是一种类型里有,一种类型里没有可以使用 in
语法
bad ❌
ts
type girl = { read: () => void };
type boy = { write: () => void };
function test(person: Fish | Bird) {
if ((person as girl).read) {
person.read();
}else {
return animal.write();
}
}
after good ✅
ts
type girl = { read: () => void };
type boy = { write: () => void };
function test(person: Fish | Bird) {
if ("read" in person) {
person.read();
return
}
animal.write();
}
用enum代替重要常量对象(TS)
对于一些多处会用到的公共常量状态标识等,我们建议分别对key和值定义枚举,提高编码规范和复用性。
bad ❌
javascript
export const statusStep: : Record<string, '0' | '1' | '2'> = {
init: '0',
'waiting_submit_enterprise_data': '1',
FAIL: '2',
SUCCESS: '2'
}
after good ✅
javascript
export enum EOnboardingStatus {
/** 初始化状态,进入用户签约页面 */
INIT = 'INIT',
/** 等待提交 */
WAIT_SUBMIT = 'waiting_submit_enterprise_data',
/** 入驻失败 */
FAIL = 'FAIL',
/** 审核成功 */
SUCCESS = 'SUCCESS',
}
export enum EOnboardingStep {
START = '0',
MID = '1',
END = '2'
}
export const statusStep: Record<EOnboardingStatus, EOnboardingStep> = {
[EOnboardingStatus.INIT]: EOnboardingStep.START,
[EOnboardingStatus.WAIT_SUBMIT]: EOnboardingStep.MID,
[EOnboardingStatus.FAIL]: EOnboardingStep.END,
[EOnboardingStatus.SUCCESS]: EOnboardingStep.END,
};