实现千分位格式化、模板字符串渲染和 Cookie 解析。

先理解原理和示例,再练习完整口述与追问。

千分位:先用正则拆出符号、整数与小数部分,只对整数部分用 \B(?=(\d{3})+(?!\d)) 插入逗号;业务中优先 Intl.NumberFormat。模板渲染:正则匹配 {{ path }},按点路径从数据中取值,只读取自有属性,缺失时保留占位符,null 渲染为空字符串。Cookie 解析:按分号拆分,按第一个等号切分键值,同名取第一个,解码失败保留原值。

\B 匹配非单词边界,确保不在数字开头插入逗号;先行断言 (?=(\d{3})+(?!\d)) 要求后面的数字个数是 3 的倍数。如果对整个字符串(含小数)应用这个正则,小数部分也会被插入逗号,所以必须拆开处理。科学计数法、NaN 等无法匹配的输入直接抛错。

模板取值时,如果允许读取原型链,{{ constructor }} 或 {{ __proto__ }} 会暴露内部对象,所以只读自有属性。渲染结果插入 HTML 前还需要转义,这里只负责字符串替换。

Cookie 值本身可能包含等号(Base64),所以只按第一个等号切分。浏览器发送的 Cookie 中,Path 更具体的同名 Cookie 排在前面,通常取第一个。用 Object.create(null) 作为结果对象,避免名为 __proto__ 的 Cookie 修改原型。

js
function formatThousands(value) {
  const match = /^(-?)(\d+)(\.\d+)?$/.exec(String(value))
  if (!match) throw new TypeError(`无法格式化:${value}`)
  const [, sign, integer, fraction = ''] = match
  return sign + integer.replace(/\B(?=(\d{3})+(?!\d))/g, ',') + fraction.replace(/\.?0+$/, '')
}

function render(template, data) {
  return template.replace(/\{\{\s*([\w.]+)\s*\}\}/g, (placeholder, path) => {
    let value = data
    for (const key of path.split('.')) {
      if (value == null || !Object.prototype.hasOwnProperty.call(Object(value), key)) return placeholder
      value = value[key]
    }
    return value == null ? '' : String(value)
  })
}

function parseCookie(cookieString) {
  const result = Object.create(null)
  for (const part of cookieString.split(';')) {
    const index = part.indexOf('=')
    if (index < 0) continue
    const name = part.slice(0, index).trim()
    if (!name || name in result) continue
    let value = part.slice(index + 1).trim()
    if (value.length >= 2 && value.startsWith('"') && value.endsWith('"')) value = value.slice(1, -1)
    try {
      value = decodeURIComponent(value)
    } catch {
      // 保留无法解码的原始值
    }
    result[name] = value
  }
  return result
}

推演:formatThousands('-1234567.50') 得到 '-1,234,567.5',末尾的 0 被去掉,'1000.00' 得到 '1,000';render('你好,{{ user.name }},{{ missing }}', { user: { name: 'Ada' } }) 得到 '你好,Ada,{{ missing }}';parseCookie('token=a=b; theme=dark; token=old') 中 token 为 'a=b'。

金额展示若要求固定两位小数,不应去掉末尾的 0,应使用 Intl.NumberFormat 的 minimumFractionDigits;decodeURIComponent 遇到不完整的百分号编码会抛 URIError,不捕获会让整个解析失败。

千分位先拆出符号、整数和小数,只对整数部分用非单词边界加先行断言的正则插入逗号,业务里更推荐 Intl.NumberFormat。模板渲染用正则匹配双花括号里的路径,逐级读取自有属性,缺失时保留占位符,null 渲染为空,插入 HTML 前还要转义。Cookie 解析按分号拆分、按第一个等号切分键值,同名取第一个,用无原型对象存结果,解码失败保留原值。