ms
webfansplz opened this issue · comments
仓库:
ms -Tiny milisecond conversion utility
源码实现:
/**
* Helpers.
*/
var s = 1000;
var m = s * 60;
var h = m * 60;
var d = h * 24;
var w = d * 7;
var y = d * 365.25; // why ? 见以下解析第1点
/**
* Parse or format the given `val`.
*
* Options:
*
* - `long` verbose formatting [false]
*
* @param {String|Number} val
* @param {Object} [options]
* @throws {Error} throw an error if val is not a non-empty string or a number
* @return {String|Number}
* @api public
*/
module.exports = function (val, options) {
options = options || {};
var type = typeof val;
// 字符串类型且不等于'',走parse方法
if (type === "string" && val.length > 0) {
return parse(val);
}
// number类型 且 为一个有限数值
else if (type === "number" && isFinite(val)) {
// long选项? fmtLong: fmtShort
return options.long ? fmtLong(val) : fmtShort(val);
}
// 类型错误报错
throw new Error(
"val is not a non-empty string or a valid number. val=" +
JSON.stringify(val)
);
};
/**
* Parse the given `str` and return milliseconds.
*
* @param {String} str
* @return {Number}
* @api private
*/
// 字符串解析成数字
function parse(str) {
str = String(str);
// why? why 100? 见以下解析2,3点
if (str.length > 100) {
return;
}
// 基操正则..
var match = /^(-?(?:\d+)?\.?\d+) *(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|weeks?|w|years?|yrs?|y)?$/i.exec(
str
);
if (!match) {
return;
}
var n = parseFloat(match[1]);
var type = (match[2] || "ms").toLowerCase();
switch (type) {
case "years":
case "year":
case "yrs":
case "yr":
case "y":
return n * y;
case "weeks":
case "week":
case "w":
return n * w;
case "days":
case "day":
case "d":
return n * d;
case "hours":
case "hour":
case "hrs":
case "hr":
case "h":
return n * h;
case "minutes":
case "minute":
case "mins":
case "min":
case "m":
return n * m;
case "seconds":
case "second":
case "secs":
case "sec":
case "s":
return n * s;
case "milliseconds":
case "millisecond":
case "msecs":
case "msec":
case "ms":
return n;
default:
return undefined;
}
}
/**
* Short format for `ms`.
*
* @param {Number} ms
* @return {String}
* @api private
*/
// 直接相除四舍五入取整
function fmtShort(ms) {
var msAbs = Math.abs(ms);
if (msAbs >= d) {
return Math.round(ms / d) + "d";
}
if (msAbs >= h) {
return Math.round(ms / h) + "h";
}
if (msAbs >= m) {
return Math.round(ms / m) + "m";
}
if (msAbs >= s) {
return Math.round(ms / s) + "s";
}
return ms + "ms";
}
/**
* Long format for `ms`.
*
* @param {Number} ms
* @return {String}
* @api private
*/
// long选项其实就是单位描述更加详细完整
function fmtLong(ms) {
var msAbs = Math.abs(ms);
if (msAbs >= d) {
return plural(ms, msAbs, d, "day");
}
if (msAbs >= h) {
return plural(ms, msAbs, h, "hour");
}
if (msAbs >= m) {
return plural(ms, msAbs, m, "minute");
}
if (msAbs >= s) {
return plural(ms, msAbs, s, "second");
}
return ms + " ms";
}
/**
* Pluralization helper.
*/
// 复数判断
function plural(ms, msAbs, n, name) {
// 复数判断,因为Math.round是四舍五入取整,所以此处判断使用n*1.5
var isPlural = msAbs >= n * 1.5;
// ms(89999, { long: true }) 1 minute
// ms(90000, { long: true }) 2 minutes
return Math.round(ms / n) + " " + name + (isPlural ? "s" : "");
}
解析:
收获:
其实 ms 的源码非常精简,也非常易读,但是我却从它的 History Commits 榨出了一些知识点:
第一版的 ms 代码其实是更简单的,当然了也存在一些问题。后面随着迭代和一些大佬(我在这里也看到了 tj 大佬,tj 大佬真是无处不在啊...)的 pr 贡献,发生了以下变化:
- 更好的可读性 (函数/变量命名)
- 更强的易用性 (unit 参数更多样化,暴露 long 参数)
- 更严谨的边界判断及优化 (见上面 2,3 点解析)
- 抛出异常 (哈哈,一开始的几个版本,没做参数的类型校验异常错误抛出)
看源码,真的也能从它的 History Commits 学到很多~
100 多行,2kb 多的库,却有 3.2k 的 star,足已见得它的实用与易用。