logo
Published on

在JavaScript中将字符串首字母大写的方法

Authors
  • Name
    Twitter

在这个技术博客中,我们将探讨如何在JavaScript中将字符串的首字母大写。这样一个看似简单的问题,由于字符编码和国际化支持的复杂性,实际上具有许多隐藏的细节。我们将深入这些细节,并提供一些实用的代码示例来解决这些问题。

问题概述和解决方案

大多数提出的函数类似于以下代码:

function capitalizeFirstLetter(str) {
  return str[0].toUpperCase() + str.slice(1)
}

但是,有些字符超出了BMP(基本多文种平面,代码点U+0到U+FFFF)。例如,处理Deseret文本时:

capitalizeFirstLetter('𐐶𐐲𐑌𐐼𐐲𐑉') // "𐐶𐐲𐑌𐐼𐐲𐑉"

这里的第一个字符无法大写,因为字符串的数组索引属性无法访问“字符”或代码点,它们访问的是UTF-16代码单元。

从ES2015起,处理这种情况变得稍微简单了一点。String.prototype[@@iterator]可以按照代码点迭代字符串。因此,我们可以使用以下代码:

function capitalizeFirstLetter([first = '', ...rest]) {
  return [first.toUpperCase(), ...rest].join('')
}

capitalizeFirstLetter('𐐶𐐲𐑌𐐼𐐲𐑉') // "𐐎𐐲𐑌𐐼𐐲𐑉"

对于较长的字符串,此方法可能效率不高,因为我们不需要遍历剩余部分。我们可以使用String.prototype.codePointAt来获取第一个(可能的)字母,但仍需要确定切片的起始位置。

处理非BMP字符

function capitalizeFirstLetter(str) {
  if (!str) return ''

  const firstCP = str.codePointAt(0)
  const index = firstCP > 0xffff ? 2 : 1

  return String.fromCodePoint(firstCP).toUpperCase() + str.slice(index)
}

capitalizeFirstLetter('𐐶𐐲𐑌𐐼𐐲𐑉') // "𐐎𐐲𐑌𐐼𐐲𐑉"

国际化考虑

处理国际化时,有些语言需要特定的处理。例如,土耳其语中的“I”大写为“İ”,而小写形式的“I”是“ı”。

function capitalizeFirstLetter([first = '', ...rest], locale) {
  return [first.toLocaleUpperCase(locale), ...rest].join('')
}

capitalizeFirstLetter('italy', 'en') // "Italy"
capitalizeFirstLetter('italya', 'tr') // "İtalya"

在浏览器中,可以使用navigator.language来获取用户首选的语言标签,在多语言文档中,可以使用Object(element.closest('[lang]')).lang来获取特定DOM元素的语言。

在支持Unicode字符属性类的环境中,可以进一步优化:

function capitalizeFirstLetter(str, locale = navigator.language) {
  return str.replace(/^\p{CWU}/u, (char) => char.toLocaleUpperCase(locale))
}

通过CSS和HTML中的lang属性及text-transform属性,即使没有JavaScript,也可以处理文字的显示:

<!doctype html>
<dl>
  <dt>未转换</dt>
  <dd>ijsselmeer</dd>
  <dt>使用CSS和<code>lang=en</code>大写</dt>
  <dd lang="en" style="text-transform: capitalize">ijsselmeer</dd>
  <dt>使用CSS和<code>lang=nl</code>大写</dt>
  <dd lang="nl" style="text-transform: capitalize">ijsselmeer</dd>
</dl>

在Chromium中,英文和荷兰文的输出都为Ijsselmeer。但是,在当前的Firefox中,我们可以看到荷兰文会正确显示为IJsselmeer

总的来说,即使这些国际化问题目前没有影响到你,但了解它们是很有必要的,因为你最终可能会遇到。