一、背景
有一个延迟时间的字段数据,后端默认返回的是ms,但是返回的时候有可能数据量较大,所以需要前端根据后端返回的值去换算单位。
二、代码版本更迭
1. 第一版
思路: 根据后端返回的值和换算的临界值去判断。
缺点: 用了大量的if/else,代码不够简洁
export const timeConvert = function (timeValue) {
if (isNaN(timeValue)) {
return '';
}
const baseUnitLabel = ['ms', 's', 'min', 'h'];
const basicValue = [1,1000,1000*60,1000*60*60]
const exp1 = Math.floor(timeValue / 1000);
let exp2;
if (exp1 < 1) {
exp2 = 0
} else if (exp1 < 60) {
exp2 = 1
} else if(exp1 < 60 * 60) {
exp2 = 2
} else {
exp2 = 3
}
let resValue = timeValue/basicValue[exp2]
if (resValue.toString().length > resValue.toFixed(2).toString().length) {
resValue = resValue.toFixed(2);
}
return `${resValue} ${baseUnitLabel[exp2]}`;
};
2. 第二版
思路: 引入Math.log(value)/Math.log(60),利用指数和下标关联起来。
缺点: 临界单位的换算关系并不是都是1/60的关系,比如ms到s就是1/1000,需要分两种情况去分析考虑
优点: 省去了大量的if/else
相关代码如下:
export const timeConvert = function (timeValue) {
if (isNaN(timeValue) || typeof timeValue != 'number') {
return '';
}
const baseUnitLabel = ['ms', 's', 'min', 'h'];
const baseUnitValue = [1,1000,1000*60,1000*60*60]
const formatValue = Math.floor(timeValue / 1000);
let resIndex;
if (formatValue < 1) {
resIndex = 0
} else {
const value = Math.floor(Math.log(formatValue) / Math.log(60)) + 1;
resIndex = value > 3? 3: value
}
let resValue = timeValue / baseUnitValue[resIndex]
if (resValue.toString().length > resValue.toFixed(2).length) {
resValue = resValue.toFixed(2);
}
return `${resValue} ${baseUnitLabel[resIndex]}`;
};
3. 终版
思路: 引入findLastIndex及Math.max代提if/else;Number方法可以自动将诸如'2.00'转换成2;
优点:代码更简洁、可读性更强
export const timeConvert = function (timeValue) {
if (!(typeof timeValue === "number" && timeValue >= 0)) {
return "";
}
const baseUnitLabel = ["ms", "s", "min", "h"];
const basicValue = [1, 1000, 1000 * 60, 1000 * 60 * 60];
let index = Math.max(basicValue.findLastIndex(basic => timeValue >= basic), 0)
return `${Number((timeValue / basicValue[index]).toFixed(2))} ${baseUnitLabel[index]}`;
};
三、知识点总结
1. Math.log
比如字节数的转换,就可以利用Math.log(bytes)/Math.log(1024)将其和单位的映射对应起来。
适应场景:适合相邻单位换算关系倍数固定的场景
2. findLastIndex
const da = [1,2,3,4,5]
const sa = da.findLastIndex(item => item < 4) // 2
如果找不到符合的值,返回-1;
结合上述案例
let index = Math.max(basicValue.findLastIndex(basic => timeValue >= basic), 0)
相当于
if (exp1 < 1) {
exp2 = 0
} else if (exp1 < 60) {
exp2 = 1
} else if(exp1 < 60 * 60) {
exp2 = 2
} else {
exp2 = 3
}
为什么用findLastIndex,而不用findIndex是为了缩小范围,找到确定的值,如果最大下标对应的值符合条件,则返回。
3. 为什么用Number?
return `${Number((timeValue / basicValue[index]).toFixed(2))} ${baseUnitLabel[index]}`;
原因: 因为tofix(2)已经转换成字符串了,且有可能有诸如2.003转换成2.00的形式,2.00的格式不准确,应为2。
而Number('2.00')可以转换成2,省去了如下的判断
if (resValue.toString().length > resValue.toFixed(2).length) {
resValue = resValue.toFixed(2);
}