前端技术栈 有关前端的博客

JavaScript 时间格式化的最优方案:从踩坑到优化的终极指南

2025-04-03
皇甫春风
js

JavaScript 时间格式化的最优方案:从踩坑到优化的终极指南

在代码 review 过程中,我们经常会遇到时间格式化的问题。一个高效、灵活、可维护的 formatDate 方法至关重要。本文将带你从基础实现到优化改进,最终打造最优版本,同时也会深入解析 substrsubstringslice 的区别及最佳用法。


一、时间格式化的优化过程

第一版:基础实现

第一版使用 RegExp 处理格式化逻辑,代码结构较为直观,但存在 substr 过时、正则匹配效率优化空间等问题。

public static formatDate(date: Date, fmt: string) {
  const o: { [name: string]: number } = {
    "M+": date.getMonth() + 1, // 月份
    "d+": date.getDate(), // 日
    "h+": date.getHours(), // 小时
    "m+": date.getMinutes(), // 分
    "s+": date.getSeconds(), // 秒
    "q+": Math.floor((date.getMonth() + 3) / 3), // 季度
    S: date.getMilliseconds(), // 毫秒
  };

  if (/(y+)/.test(fmt)) fmt = fmt.replace(RegExp.$1, (date.getFullYear() + "").substr(4 - RegExp.$1.length));
  for (const k in o)
    if (new RegExp("(" + k + ")").test(fmt))
      fmt = fmt.replace(RegExp.$1, RegExp.$1.length === 1 ? `${o[k]}` : `00${o[k]}`.substr(("" + o[k]).length));
  return fmt;
}

缺陷分析:

  1. substr 已被废弃,应使用 slicesubstring 代替。
  2. 正则表达式直接操作 RegExp.$1 可能不稳定,推荐使用 match 回调替换。
  3. 格式化补零逻辑不够优雅,可以优化字符串处理方式。

第二版:优化代码结构

第二版采用 substring 代替 substr,并提升 RegExp 处理方式,使代码更清晰。

public static formatDate(format: string, date = new Date()) {
  const o = {
    'M+': date.getMonth() + 1, // 月份
    'D+': date.getDate(), // 日
    'h+': date.getHours(), // 时
    'm+': date.getMinutes(), // 分
    's+': date.getSeconds(), // 秒
    'q+': Math.floor((date.getMonth() + 3) / 3), // 季度
    S: date.getMilliseconds(), // 毫秒
  }
  // 处理年份
  if (/Y+/.test(format)) {
    format = format.replace(/Y+/, match => (date.getFullYear() + '').substring(4 - match.length))
  }
  // 处理其他格式
  for (const [k, v] of Object.entries(o)) {
    const sv = v.toString()
    const reg = new RegExp(`(${k})`)
    if (reg.test(format)) {
      format = format.replace(reg, match => (match.length === 1 ? sv : `00${sv}`.substring(sv.length)))
    }
  }
  return format
}

优化点:

  1. substr 替换为 substring,提高兼容性。
  2. 使用 match 回调替换字符串,避免直接操作 RegExp.$1
  3. Object.entries(o) 方式遍历 o,让代码更现代化。

最终版:终极优化

最终版采用 slice 进一步简化字符串操作,提高可读性和执行效率。

public static formatDate(date: Date, fmt: string): string {
  const o: { [key: string]: number } = {
    "M+": date.getMonth() + 1, // 月份
    "d+": date.getDate(), // 日
    "h+": date.getHours(), // 小时
    "m+": date.getMinutes(), // 分钟
    "s+": date.getSeconds(), // 秒
    "q+": Math.floor((date.getMonth() + 3) / 3), // 季度
    S: date.getMilliseconds(), // 毫秒
  };

  // 处理年份
  if (/y+/.test(fmt)) {
    fmt = fmt.replace(/y+/, (match) => date.getFullYear().toString().slice(-match.length));
  }

  // 处理其他格式
  for (const k in o) {
    const regex = new RegExp(`(${k})`);
    if (regex.test(fmt)) {
      fmt = fmt.replace(regex, (match) => (match.length === 1 ? `${o[k]}` : `00${o[k]}`.slice(-match.length)));
    }
  }

  return fmt;
}

最终优化点:

  1. substring 改为 slice,让代码更简洁高效
  2. 使用 slice(-match.length) 直接获取年份的后 N 位,避免复杂计算。
  3. 补零逻辑优化:使用 slice(-match.length) 替代 substring,使代码更直观。

二、substrsubstringslice 的正确用法

在时间格式化过程中,我们用到了 substrsubstringslice,它们的区别如下:

方法 语法 作用 关键区别
substr str.substr(start, length) start 位置开始,截取 length 长度的字符串 已废弃,不建议使用
substring str.substring(start, end) 截取 startend(不包括 end)的部分 start > end 时会自动交换参数
slice str.slice(start, end) 截取 startend(不包括 end)的部分 支持负数索引,更推荐使用

最佳实践:

  • slice 是最推荐的,尤其在处理负数索引时更灵活。
  • substring 适用于简单截取,但不能处理负数索引
  • 避免使用 substr,因为它已被废弃,未来可能被移除。

示例代码:

const str = "JavaScript";

// substring: 取 "Java"
console.log(str.substring(0, 4));

// slice: 取 "Script"
console.log(str.slice(-6));

// substr(不推荐): 取 "Java"
console.log(str.substr(0, 4));

总结

  1. 时间格式化方法的优化
    • 第一版:基础实现,但使用了过时方法。
    • 第二版:优化代码结构,使用 substring,提高可读性。
    • 最终版:采用 slice,代码更简洁高效。
  2. substrsubstringslice 的正确用法
    • 推荐使用 slice,避免 substr

通过这次优化,我们得到了一个高效、现代、易维护formatDate 方法。希望这篇文章能帮助你在实际项目中写出更优雅的代码!🚀


类似文章

评论