js中的localeCompare到底是如何比较的?

本文详细介绍了JavaScript中数组的sort方法及其比较函数的使用,包括按照数字和ASCII码排序的规则。重点讨论了localeCompare方法,它用于基于国际化字符表进行排序,能处理中文和英文的排序,尤其在处理中文拼音排序时的应用。文章还探讨了localeCompare在处理多音字和指定拼音首字母筛选汉字的场景,并举例说明了其工作原理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

    js中有一个sort方法,可以用来对数组排序,它有一个比较函数,用来比较两个元素的大小,我们先来看一个最基本的:

['2','10','3'].sort((a,b)=>{
    return a-b;
});

    上面我们写了一个最简单的sort,它的返回结果是['2','3','10']。

    上面的排序规则是先将字符串转成数字,然后按照数字大小排序的,因为字符串在遇到减号的时候,先转成Number,再进行计算。

    除了上面这种排序,还有一种排序规则:

['2','10','3'].sort((a,b)=>{
    return a>b?1:-1;
});

    上面排序的结果是["10", "2", "3"],为啥在这里’10‘是最小的?

    因为这里是按照ASCII码的大小进行排序的,‘10’第一个字符的ASCII码是49,'2'的ASCII码是50,因此'10'是要比'2'小。我们在浏览器中输入'10'<'2',可以发现是true。

    除了上面两种常见的比较,还有一种比较方法localeCompare。

    localeCompare是个啥东东

    localeCompare是一种基于国际化字体的地区字符比较,例如中国用中文,美国用英文,法国用法文,德国用德文。。将这些国家的文字按照国家/地区等进行编号,然后每个编号都对应了该国/地区的文字。

    简单一点理解,就是你可以想象有一个map,key是这些国家/地区的编号,例如汉字,zh-CN;英文,en-US等,它们的value就对应了改国/地区的文字。   

    我们之前讲过一篇文章:js中的UTF-8编码与解码,里面有一个Unicode字符表,它是将全世界所有的语言文字统一进行编码,它和这里讲的国际化字符表不一样。

    Unicode字符表的目的是将所有的文字/符号进行编码,这样任意一个符号都可以通过一个编号进行表示,里面除了有全世界所有的文字,还有表情符号等;而国际化字符表的目的是为了区分不同国家/地址的文字,例如你的页面现在是中文的,想翻译成英文版的,那么就切换到英文的文字,然后用英文对他进行翻译。

    也就是Unicode字符表中无法区分字符是哪个国家的,而国际化字符表可以区分不同语言。

    注意这两个表除了用途不一样,对字符的编码顺序也是不同的,这点很重要,我之前就是没有理解这一点,因此有些问题无法理解。

    数字/英文的顺序

    ASCII码我们平时听的比较多,它会对数字/符号/英文字符进行编号,例如'a'是97,'A'是65等,我们看看常见的数字/英文的ASCII排序顺序:

'0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'.split('').sort((a,b)=>a>b?1:-1).join('');
//结果:
0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz

    我们可以看到,用>号比较的就是按照ASCII码的排序顺序。那么我们来看localeCompare的结果:

'0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'.split('').sort((a,b)=>a.localeCompare(b)).join('');
//结果:
0123456789aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ

    我们可以看到最显著的差别就是在国际化字符表中,英文的顺序是小写/大写,小写/大写,。。。而上面的ASCII码是大写,大写,。。小写,小写。。。

    这会导致什么问题呢?我之前看到有这样一个问题:

'B'.localeCompare('a');
//结果
1

    上面的意思是说,在字符表中,B在a的后面,他就怎么都想不通,为啥B在a的后面?在ASCII表中,B明显在a的前面啊,怎么跑到后面去了?

    因为他没有搞清楚ASCII表和国际字符表是不同的,如果你想验证在ASCII码的关系,正确的简单验证方法如下:

'B'<'a'
//结果:
true

    上面验证的才是ASCII表中的顺序。

    中文的顺序   

    localeCompare被提及最多的就是可以将中文按照拼音的顺序进排序,那我们现在就来验证一下,它对中文的排序到底如何。

    这里我们要借助我们小时候背的字母歌来进行测试,我们先来个普通的测试:

'啊波次的俄佛歌,何以饥渴了么呢,我泼漆,日斯特,无谓网,洗鸭子'.split('').sort((a,b)=>a.localeCompare(b)).join('');
//结果:
,,,,,么了以何佛俄呢啊子我斯无日次歌波泼洗渴漆特的网谓饥鸭

    我们看到,在默认的情况下,localeCompare并没有按照拼音的顺序排序,那么这是什么顺序?猜测可能是unicode码顺序,我们测试下:

'啊波次的俄佛歌,何以饥渴了么呢,我泼漆,日斯特,无谓网,洗鸭子'.split('').sort((a,b)=>a.charCodeAt(0)-b.charCodeAt(0)).join('');
//结果:
么了以何佛俄呢啊子我斯无日次歌波泼洗渴漆特的网谓饥鸭,,,,,

    我们可以看到,默认情况下,localeCompare是按照unicode字符标的顺序来的。同时我们还可以发现,在unicode字符标中,符号在文字后面;在国际字符表中,符号在文字前面。

    localeCompare还有一个参数,表示参照那个国家/地区的字符标表,我们设置为中文('zh-CN'),我们再测试一次:

'啊波次的俄佛歌,何以饥渴了么呢,我泼漆,日斯特,无谓网,洗鸭子'.split('').sort((a,b)=>a.localeCompare(b,'zh-CN')).join('');
//结果
,,,,,啊波次的俄佛歌何饥渴了么呢泼漆日斯特网谓我无洗鸭以子

    我们看到大部分按照拼音了,但是好像有几个汉字乱了,那是因为有几个字母开头的没有汉字,是模拟的汉字,如:‘以’模拟i,‘我’模拟o,‘无'模拟u,‘谓'模拟v,认真比对后发现,确实是按照拼音来的。

    我们再测试一个:

'一二三四五六七八九十'.split('').sort((a,b)=>a.localeCompare(b,'zh-CN')).join('');
//结果
八二九六七三十四五一
拼音如下:
ba er jiu liu qi san shi si wu yi

    我们可以发现,localeCompare确实是按照拼音顺序来的。

    中文多音字

    多音字是比较麻烦的,localeCompare无法正确比较多音字,例如凹,可以读作ao或者wa,wa是少数读音,ao是多数读音,比较的时候只能比较出作为ao的读音,无法比较出wa的读音,例如:

'啊'.localeCompare('凹','zh-CN');//-1,啊在凹前面
'吧'.localeCompare('凹','zh-CN');//1//吧在凹后面

     上面的测试说明凹的读音介于a,b之间,读作ao。

    指定汉字拼音首字母

    利用localCompare的特性,我们可以得到指定拼音首字母的汉字,代码如下:

function sort(array, key) {
  if(!key.toLocaleLowerCase().match(/[a-z]/ || !Array.isArray(array) || array.length === 0)) {
    throw Error('传的参数有问题')
  }
  function compare(a, b) {
    return a.localeCompare(b, 'zh-CN')
  }
  const letters = 'abcdefghjklmnopqrstwxyz'.split(''),
    zh = '阿八嚓哒妸发旮哈讥咔垃痳拏噢妑七呥扨它穵夕丫帀'.split(''),
    index = key && letters.indexOf(key.toLocaleLowerCase()),
    result = []
  array.forEach(value => {
    if(value[0].toLocaleLowerCase() === key.toLocaleLowerCase() || compare(zh[index],value[0]) <= 0 && (!zh[index + 1] || compare(value[0], zh[index+1]) < 0)) {
      result.push(value)
    }
  })
  result.sort((a, b) => compare(a, b))
  return result
}
sort(['啊','波','次','得'],'a');
["啊"]

    上面指定拼音首字母为a,那么可以将其他的汉字过滤掉。

    它的原理就是找到以26个字母为拼音开头的第一个汉字。这里是'阿八嚓哒妸发旮哈讥咔垃痳拏噢妑七呥扨它穵夕丫帀'。

    然后判断它们的前后关系,例如以a开头的,用localeCompare比较,汉字在'阿'后面,'八'前面的,肯定就是a开头了。

    可能有人要问了,你咋知道这几个汉字代表它拼音首字母的第一个汉字呢?这个很简单,在‘新华词典’上找,我们可以找一个在线的:在线汉语词典,我们截个图看看:

    

    上面罗列了所有的可读拼音,我们只要看每个字母第一个可读的,例如我们随便找一个:

    例如r开头的第一个是ran(2声),我们得到了13个汉字,我们再用localeCompare计算出里面最小的那个即可得到r开头最小的汉字。其实我们基本可以发现,第一排第一个汉字就是最小的(可能按照比划来的)。

   但是同样注意,有多音词的:

    

    按道理来说夹在旮前面,但是因为夹是多音词,因此夹被放在了j开头的拼音里面(jia)。因此g开头第一个汉字是旮。

转载于:http://www.qiutianaimeili.com/html/page/2020/07/20207171bfbctvh896.html

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值