0%

dayjs源码解析(五):插件(下)


年末一直在抽空学习 Chrome 扩展开发以及 DartFlutter 的移动端开发,两个多月没有更新文章。在这里顺便把 2021 的学习计划更新下。

春节期间已经实现了一个自定义配置的多引擎搜索扩展 Rummage,下一篇分享下扩展的设计、开发和发布,再进一步学习计划如下:

  • 学习 TypeScript,抽空开发一个配色应用,名字还没想好,应用应该内置大量经典配色盘,还可以将任意图片量化为一组配色并保存,而且用 TypeScript 实现 Chrome 扩展和用 Flutter 实现 移动端APP 两个平台。
  • 源码阅读计划:继续完成 lodash 的源码分析,学习 Vue3ElementUI 的源码实现。
  • 地铁读书计划:早晚地铁读《学习 Javascript 数据结构与算法》、《Javascript 设计模式与开发实践》、《Flutter 实战》和重读《Javascript 高级程序设计》
  • 工具学习计划WebpackGit 的深入学习。

接上篇 —— dayjs 源码解析(三):插件(中) —— 继续解析 dayjs 的源码。

剩余的插件功能比较零散,实现起来也比较简单,所以解析的代码不全部放在文章里了,简单介绍下剩余每个插件的功能:

  • advancedFormat :实现更复杂的格式化;
  • arraySupport :实现对数组结构参数的支持;
  • badMutable :由不可变对象转变成可变对象;
  • buddhistEra :实现东南亚佛历的格式化;
  • calendar :实现日历;
  • dayOfYear :返回一个 number 来表示日期是年中第几天,或设置成是年中第几天;
  • devHelper :开发时插件,显示一些提示和警告方便开发。;
  • localDatalocalizedFormat :实现本地化(这一块看的有点懵);
  • minMax :挑选最大值或最小值;
  • pluralGetSet :给每个单位添加复数形式方法,与非复数同名函数一致;
  • toArraytoObject :返回时间数组或对象;
  • updateLocale :更新指定语言的任何属性,而其他属性将会保持不变 ;

具体的分析已经放在了 Github 中,感兴趣可以移步对应文件。本篇作为 dayjs 源码解析系列的最后一篇,来动手实现一个自己的插件。

目录如下:

  1. dayjs 源码解析(一):概念、locale、constant、utils
  2. dayjs 源码解析(二):Dayjs 类
  3. dayjs 源码解析(三):插件(上)
  4. dayjs 源码解析(四):插件(中)
  5. dayjs 源码解析(五):插件(下)

手写一个 Dayjs 插件

作为一个示范插件,应该尽量实现简单。咱们就来动手写一个按照指定范围随机取一个时刻的方法。

插件设计

  • 实现静态方法 randBetween,必传参数为 2 个时刻,返回 2 个时刻间的 1 个随机时刻。
  • 实现类实例的原型方法 randBetween, 必传参数为 1 个时刻,返回该时刻与实例时刻间的 1 个随机时刻。
  • 入参的时刻类型可以为 Dayjs 实例、Date 实例或 13 位时间戳,返回的时刻类型为 Dayjs 实例。

实现

实现的思路如下:

  1. 两个时刻统一封装为 Dayjs 实例
  2. 取到两个时刻的 13 位时间戳。(valueOf
  3. 计算两个时间戳内的随机整数
  4. 将随机事件封装为 Dayjs 实例返回

工具函数:计算随机整数

按照计算机科学的惯例,应当使用前闭后开。

1
2
3
4
5
6
7
/**
* @description: 计算随机整数
* @param {Number} min 最大值
* @param {Number} max 最小值
* @return {Number} 前闭后开
*/
const random = (min, max) => Math.floor(Math.random() * (max - min)) + min;

工具函数:求随机时刻

dayjs().valueOf() 方法可以直接取到 13 位的时间戳。

1
2
3
4
5
6
7
8
9
10
11
12
/**
* @description: 计算随机时刻
* @param {Dayjs| Date | Number} ref1 两端时刻
* @param {Dayjs| Date | Number} ref2 两端时刻
* @return {Dayjs} 返回一个Dayjs实例
*/
const between = (ref1, ref2) => {
let val1 = dayjs(ref1).valueOf();
let val2 = dayjs(ref2).valueOf();
let randomResult = random(...[val1, val2].sort((a, b) => a - b));
return dayjs(randomResult);
};

挂载到 dayjs 函数对象和类实例原型上

最后一步,就是将方法挂载到需要的地方。挂载到原型上时需要注意不可以用箭头函数,因为需要 this 指向实例。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
* @description: plugin
* @param {Object} option option
* @param {Class} Dayjs Dayjs类
* @param {Function} dayjs dayjs函数对象
*/
let dayjsRandom = (option, Dayjs, dayjs) => {
const random = (min, max) => Math.floor(Math.random() * (max - min)) + min;

const between = (ref1, ref2) => {
let val1 = dayjs(ref1).valueOf();
let val2 = dayjs(ref2).valueOf();
let randomResult = random(...[val1, val2].sort((a, b) => a - b));
return dayjs(randomResult);
};

Dayjs.prototype.randBetween = function (ref) {
return between(this, ref);
};
dayjs.randBetween = (ref1, ref2) => between(ref1, ref2);
};

测试

2021年1月1日0时0分0秒13 位时间戳为 1609430400000

1
2
3
4
5
6
7
8
// 挂载插件
dayjs.extend(dayjsRandom);
// 实例测试
dayjs().randBetween(new Date(1609430400000)).format(); // output: 2021-01-27T06:53:42+08:00
dayjs().randBetween(dayjs(1609430400000)).format(); // output: 2021-02-04T02:56:27+08:00
dayjs().randBetween(1609430400000).format(); // output: 2021-01-22T02:38:09+08:00
// 函数对象测试
dayjs.randBetween(new Date(), 1609430400000).format(); // output: 2021-01-04T01:30:56+08:00

三种格式的输出都没有问题。

总结

dayjs 源码解析系列完成 🎉。该系列先解析了关于时间的一些概念,又分解了 dayjs 项目的结构并分析了 Dayjs类 的源码,再解析了各个插件的源码,最后手动实现了一个插件。


前端记事本,不定期更新,欢迎关注!


👆 全文结束,棒槌时间到 👇