最近在翻看原来写的工具函数中,发现了formatDate这个工具函数,原来只是在使用,具体的实现原理没有仔细研究过,这次就来分析一下它的实现,了解一下正则表达式的一个实战应用。

函数功能

formatDate 函数接受两个参数,日期对象和格式参数,可以根据不同的格式参数将事件对象转换成日期格式,大致使用如下

formatDate(new Date(), 'yy-mm-dd')    // 18-05-13
formatDate(new Date(), 'yyyy-MM-dd hh:mm') // "2018-05-13 16:35"

当然也可以把函数挂载在prototype上面,这样就只需要一个参数了,这个后续再说。

实现思路

实现思路比较简单,就是利用,Date提供的函数,比如getMonthgetFullYear等函数先把Date对象拆解出必要的部分。年月日小时分钟秒等,然后再根据传入的参数进行组合,得到想要的效果。这里有几个处理过程中需要注意的地方。

  • 对于1位的数字应该可以支持选择补0也可以不补0
formatDate(new Date(), 'yyyy-M-dd') // "2018-5-13"
formatDate(new Date(), 'yyyy-MM-dd') // "2018-05-13"
  • 对于月份因为是从0开始计数的,所以要+1月,不是加1s啊
  • 支持参数的各种组合

实现过程

因为需要支持各种组合,所以需要一个对象将个部分存储下来,根据参数的格式能够快速的解析出来对应的值。对象的格式大致如下

var cfg = {
    yyyy: date.getFullYear(), // 年 : 4位
    yy: date.getFullYear().toString().substring(2), // 年 : 2位
    M: date.getMonth() + 1, // 月 : 如果1位的时候不补0
    MM: paddNum(date.getMonth() + 1), // 月 : 如果1位的时候补0
    d: date.getDate(), // 日 : 如果1位的时候不补0
    dd: paddNum(date.getDate()), // 日 : 如果1位的时候补0
    hh: paddNum(date.getHours()), // 时
    mm: paddNum(date.getMinutes()), // 分
    ss: paddNum(date.getSeconds()) // 秒
  }

双位补0单位不补0,补0的过程封装成一个函数paddNum,自然想到的是用正则表达式来判断是否是1位,很容易写出如下代码

let paddNum = num => {
  if (/^\d$/.test(num)) {
     return '0' + num
  }
  return num
}

不过还可以使用字符串提供的正则表达式replace功能简化这个过程,使用捕获组来获取匹配的值,再+0,如果没有匹配,就返回本身,这也正是我们想要的,这里提前需要把数字转换成字符串。

let paddNum = num => num.toString().replace(/^(\d)$/, '0$1')

这样每个日期的数据处理部分就做完了。
下面实现日期格式的解析,要把传入的日期格式做匹配,然后从对象解析出对应的值,再拼到一块。
分析传入日期的格式,都是yyyydd这种重叠的格式,这就需要针对每一个匹配出来的值,都要匹配到与其相同的一组值,也就是如果匹配到y,那就要把紧跟在y后面所有的y都匹配出来,比如yyyyyy等,直到遇到首个不是y的字符停止。这就需要在匹配环节记住匹配的部分并重复匹配,捕获组又派上了用场。 /([a-z])(\1)*/\1可以代替匹配到的字符,进而组成新的匹配条件,这样的匹配条件就是那种想要的重叠格式了。
所以解析的过程如下

format.replace(/([a-z])(\1)*/ig, m => cfg[m])

整合

稍作整理,这个工具函数就组合起来了

const formatDate = (date=new Date(), format='yyyy-MM-dd hh:mm:ss') => {
  // 单位补0
  let paddNum = num => num.toString().replace(/^(\d)$/, '0$1')
  // 指定格式字符
  var cfg = {
    yyyy: date.getFullYear(), // 年 : 4位
    yy: date.getFullYear().toString().substring(2), // 年 : 2位
    M: date.getMonth() + 1, // 月 : 如果1位的时候不补0
    MM: paddNum(date.getMonth() + 1), // 月 : 如果1位的时候补0
    d: date.getDate(), // 日 : 如果1位的时候不补0
    dd: paddNum(date.getDate()), // 日 : 如果1位的时候补0
    h: date.getHours(), // 时
    hh: paddNum(date.getHours()), // 时
    mm: paddNum(date.getMinutes()), // 分
    ss: paddNum(date.getSeconds()) // 秒
  }
  return format.replace(/([a-z])(\1)*/ig, m => cfg[m])
}

可以看出用好正则表达式的捕获组,可以大大简化代码量,逻辑看上去也很清楚。


dongzhe3917875
1.5k 声望76 粉丝