☰
JS Tips 04:如何正确排序含重音字符的字符串(localeCompare 与 Intl.Collator 实战)
2026/10/8 7:11:09 网站建设 项目流程
  • 教程

【免费下载链接】jstips

This is about useful JS tips!

项目地址:https://gitcode.com/gh_mirrors/js/jstips
点击查看免费下载

本篇是 JS Tips 系列的第 04 篇。它揭示了一个常见的国际化(i18n)陷阱:JavaScript 原生Array.prototype.sort()只按英文字母的码点顺序排序,遇到西班牙语、德语等含重音(accented)字符的非 ASCII 字符串时会产生"错序"。读完本文,你将掌握使用String.prototype.localeCompare()与Intl.Collator两种方案按语言习惯正确排序的方法,并能针对不同区域(locale)定制排序规则,避免线上数据出现意外的排列结果。

问题:原生sort()在非 ASCII 字符串上的"错序"

JavaScript 原生提供了Array.prototype.sort()方法用于排序数组。当不传入比较函数时,sort()会把每个数组元素当作字符串,并按字母表顺序排序:

['Shanghai', 'New York', 'Mumbai', 'Buenos Aires'].sort(); // ["Buenos Aires", "Mumbai", "New York", "Shanghai"]

对纯英文(ASCII)字符串,这个结果是符合预期的。但当你尝试排序一个包含非 ASCII 字符(如重音字母é、á、ú)的数组时,就会得到一个"奇怪"的结果:

['é', 'a', 'ú', 'c'].sort(); // ['c', 'e', 'á', 'ú'] // 错序,不符合任何自然语言的字母顺序

再看两个真实的语言场景:

// 西班牙语 ['único','árbol', 'cosas', 'fútbol'].sort(); // ["cosas", "fútbol", "árbol", "único"] // 错误顺序 // 德语 ['Woche', 'wöchentlich', 'wäre', 'Wann'].sort(); // ["Wann", "Woche", "wäre", "wöchentlich"] // 错误顺序

在西班牙语中,árbol应排在cosas之前;在德语中,Wann应排在wäre之前、wöchentlich之前。但原生排序完全打乱了语言习惯。

为什么会这样?

从语言规范与实现机制可以推断:不带比较函数调用sort()时,引擎会把每个元素转换为字符串,然后逐字符按 UTF-16 码元(code unit)的数值大小比较。重音字母的 Unicode 码点远大于普通 ASCII 字母(例如é的码点是 U+00E9,大于a的 U+0061 和c的 U+0063),于是é、á、ú这类字符全部被排到了a、c之后,看起来就是"乱序"。简单说,原生sort()只"懂"英文(或者说只懂纯码点排序),不理解任何语言的重音规则、大小写规则与字母扩展规则。

解决方案一:使用String.prototype.localeCompare()

localeCompare()是字符串实例上的方法,由 ECMAScript 国际化 API(ECMA-402)提供。它按指定区域的语言规则比较两个字符串,返回一个负数、零或正数,正好可以作为sort()的比较函数:

['único','árbol', 'cosas', 'fútbol'].sort(function (a, b) { return a.localeCompare(b); }); // ["árbol", "cosas", "fútbol", "único"] ['Woche', 'wöchentlich', 'wäre', 'Wann'].sort(function (a, b) { return a.localeCompare(b); }); // ["Wann", "wäre", "Woche", "wöchentlich"]

可以看到,两组数据都恢复了正确的语言排序:árbol排到了最前,Wann也排到了德语序列的正确位置。

解决方案二:使用Intl.Collator

Intl.Collator是同一个国际化 API 提供的更专门的排序器对象。它的compare属性是一个可直接传入sort()的稳定比较函数,写法更简洁:

['único','árbol', 'cosas', 'fútbol'].sort(Intl.Collator().compare); // ["árbol", "cosas", "fútbol", "único"] ['Woche', 'wöchentlich', 'wäre', 'Wann'].sort(Intl.Collator().compare); // ["Wann", "wäre", "Woche", "wöchentlich"]

输出与localeCompare()完全一致,但省去了每次比较时临时创建函数调用的开销。

定制区域(locale)与比较规则

这两种方法都带有各自的可配置参数,以便适配不同的语言环境。

两个 API 都接受locales与options两个参数,这是它们最实用的部分:

  • locales(区域标识):例如'es'(西班牙语)、'de'(德语)、'fr'(法语)、'zh-Hans-CN'(简体中文)。不传时使用运行环境的默认区域。
// 指定西班牙语区域排序 ['único', 'árbol', 'cosas', 'fútbol'] .sort((a, b) => a.localeCompare(b, 'es')); // 用 Collator 指定德语区域 const germanCollator = new Intl.Collator('de'); ['Woche', 'wöchentlich', 'wäre', 'Wann'].sort(germanCollator.compare);
  • options(比较规则):常用配置包括sensitivity(决定比较时是否区分大小写与重音,取值如'base'、'accent'、'case'、'variant')和usage(取值'sort'或'search')等。例如sensitivity: 'base'意味着忽略大小写与重音差异,只按基本字母比较:
// 忽略大小写与重音进行排序 ['é', 'E', 'e', 'a', 'A'].sort((a, b) => a.localeCompare(b, 'fr', { sensitivity: 'base' }));

这两组参数让localeCompare()与Intl.Collator不仅能"正确排序",还能按业务需求调整"排序到多细的粒度"。

性能考量:大批量字符串优先使用Intl.Collator

根据原文档引述的官方性能说明,当需要比较大量字符串时,Intl.Collator更快。原因从实现结构上可以理解:Collator是一个可复用的排序器对象,其compare函数被一次性构建后反复调用,而localeCompare每次调用都可能重新解析区域规则。因此:

  • 排序少量数据(如几个下拉选项):两者皆可,localeCompare写法直观;
  • 排序成百上千条字符串(如用户列表、商品名、城市列表):优先创建一次Intl.Collator并复用其compare,能明显减少重复开销。

综合实战:去重 + 按语言排序的完整示例

本仓库的另一篇 JS Tips(第 26 篇:过滤并排序字符串列表)演示了先filter去重、再sort排序的组合用法。把两者结合,并换成localeCompare/Intl.Collator作为比较器,就是一个处理多语言数据列表的完整方案:

const names = ['úrsula', 'Álvaro', 'carlos', 'úrsula', 'Érika', 'álvaro']; // ES6 写法:去重后按西班牙语规则排序 const sorted = [...new Set(names)] .sort((a, b) => a.localeCompare(b, 'es')); console.log(sorted); // ["álvaro", "Álvaro", "carlos", "Érika", "úrsula"](顺序按区域规则确定)

再进一步,用Intl.Collator预构建比较器,适合数据量大、需要反复排序的场景:

const collator = new Intl.Collator('es'); const sorted = [...new Set(names)].sort(collator.compare);

仓库内的印证与扩展阅读

  • 本文对应的原始文档:英文原版,另有简体中文、繁体中文、西班牙语等多语言版本,可对照阅读;
  • README.md 的 Tips 列表将本篇列为第 04 号技巧,同系列的排序/过滤主题还可参考第 26 篇(过滤与排序字符串列表);
  • 若想进一步掌握字符串包含判断,可阅读第 15 篇(indexOf 的简化用法),其中也涉及字符串比较的边界场景;
  • 帖子格式规范参见 POST_TEMPLATE.md。

小结

当你要排序的字符串数组并非纯英文时,务必不要直接使用裸的array.sort():

  1. 小数据量:使用a.localeCompare(b),必要时传入locales与options;
  2. 大数据量:预构建Intl.Collator并复用其compare,性能更优;
  3. 多语言产品:为每种目标语言显式指定区域标识,避免依赖运行环境的默认区域导致结果不一致。

记住这条经验:排序含重音字符的字符串时使用localeCompare()或Intl.Collator,才能得到符合语言习惯的排列结果。

  • 教程

【免费下载链接】jstips

This is about useful JS tips!

项目地址:https://gitcode.com/gh_mirrors/js/jstips
点击查看免费下载

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询