应使用 Intl.Collator('zh', { numeric: true }) 预创建实例并传入 sort,因其按拼音排序、支持自然数字序、标准化且跨浏览器一致;而默认 sort 按 UTF-16 码点乱序,localeCompare 重复调用开销大且 Safari 等兼容性差。
直接结论:用 Intl.Collator 的 compare 方法作为 sort 的比较函数,指定 locale: 'zh' 和 numeric: true,就能稳定实现符合中文拼音顺序、兼顾数字自然排序的列表排序。
String.prototype.localeCompare 或默认 sort
默认 sort() 是按 UTF-16 码点排序,张(U+5F20)会排在 李(U+674E)前面,完全不符合拼音习惯;而单独用 localeCompare 虽然支持语言环境,但在数组批量排序中反复调用开销大,且无法统一控制 numeric 行为。更关键的是:Chrome 与 Safari 对 localeCompare 在中文 locale 下的拼音排序支持不一致(Safari 旧版本可能回退到笔画或 Unicode 排序),而 Intl.Collator 是标准化、可复用、可预配置的解决方案。
Intl.Collator 实例并传入 sort
必须提前创建一个 Intl.Collator 实例,再把它的 compare 方法传给 sort —— 这样避免每次比较都新建对象,性能更好,也确保所有比较使用同一套规则:
const collator = new Intl.Collator('zh', { numeric: true, sensitivity: 'base' // 忽略大小写和变音符号,适合中文场景});<p>const list = ['张三', '李四', '王五', '阿尔法', '10号选手', '2号选手'];list.sort(collator.compare); // ✅ 正确用法
注意以下几点:
locale: 'zh' 是关键,不能写成 'zh-CN' 或 'zh-Hans' —— 虽然多数浏览器兼容,但规范推荐 'zh' 才能触发拼音排序逻辑(部分 Android WebView 对子标签支持不稳定)numeric: true 让 '10号' 排在 '2号' 后面,否则字符串比较会让 '10' 排在 '2' 前面sensitivity: 'base' 可让 '你好' 和 'Nǐhǎo' 视为等价(如果数据含拼音混排),但纯中文列表可省略排序结果不符合预期,大概率是以下原因:
undefined、null 或非字符串项: Intl.Collator.compare 对非字符串参数会返回 NaN,导致排序行为不可预测。务必先清洗:list.filter(item => typeof item === 'string').sort(collator.compare)
'第1名'(全角1)或 '(测试)'。这些字符的 Unicode 归类可能干扰拼音权重。建议预处理:item.replace(/[uFF10-uFF19u3000-u303F]/g, c => String.fromCharCode(c.charCodeAt(0) - 0xFEE0))(半角化)console.log(JSON.stringify(item)) 检查是否出现 "ufeff张三" 这类开头,需用 .trim().replace(/^uFEFF/, '')
Intl.Collator 不处理多音字(如「重庆」的「重」读 chóng 还是 zhòng),它只按 Unicode 中文字符的标准化拼音排序规则执行。如果你的业务明确要求「重庆」必须排在「上海」之前(即强制按「chóng qìng」而非字典默认音),就得手动映射:
const pinYinMap = { '重庆': 'chongqing', '行长': 'hangzhang', // 避免被当作 'xinghang'};const collator = new Intl.Collator('zh', { numeric: true });<p>list.sort((a, b) => {const keyA = pinYinMap[a] || a;const keyB = pinYinMap[b] || b;return collator.compare(keyA, keyB);});
这种映射要谨慎维护——它脱离了标准排序逻辑,仅适用于极少数强业务语义场景。绝大多数中文列表,靠 new Intl.Collator('zh', { numeric: true }) 就已足够可靠。
真正容易被忽略的是:排序是否在所有目标运行时(尤其是 iOS WebView 和低端安卓浏览器)表现一致。建议在真实设备上验证 ['张', '李', '王'] 的输出顺序,而不是只信本地 Chrome 结果。