你们都知道 js
自带了一个排序方法 sort
,不少时候须要排序的时候也都直接使用了 sort
方法来排序。然而有时候 sort
的结果和预期结果仍是有些差别的。html
看下面的代码java
[1, 23, 2, 3].sort()
天然语言状况下,咱们指望的 排序结果应该是 [1,2,3,23]
,然而实际结果 [1, 2, 23, 3]
。git
首先咱们来看下各个浏览器( js 引擎) 的排序算法。github
浏览器 | 使用的 JavaScript 引擎 | 排序算法 | 源码地址 |
---|---|---|---|
Google Chrome | V8 | 插入排序和快速排序 | sort 源码实现 |
Mozilla Firefox | SpiderMonkey | 归并排序 | sort 源码实现 |
Safari | Nitro(JavaScriptCore ) | 归并排序和桶排序 | sort 源码实现 |
Microsoft Edge 和 IE(9+) | Chakra | 快速排序 | sort 源码实现 |
注:上述表格数据来源于 排序算法 |
以 v8
为例, 它分别使用了 插入排序和 快速排序,分别使用的场景 能够查看源码,也能够看 justjavac
大佬的这篇文章 ,同时这篇文章也分析了一个排序的坑。 web
当 sort
没传递 compareFn
的时候,就会使用默认的 compareFn
,而 sort
不少的坑都源于这里。 根据 justjavac
大大的那篇文章里的分析和 ecma
标准里的声明,咱们得知compareFn
会在内部尝试调用 toString
将比较的对象转化成字符串。算法
在转化成字符串这一步以后,坑就出现了。当 x
和 y
(这里 x
、 y
都已是字符串了)进行比较的,实际上是依据字典序来比较的,而比较的对象其实数据的 charCode
。segmentfault
字典序的比较方法引用一下 维基百科上的说法:浏览器
是先按照第一个字母、以 a、b、c……z 的顺序排列;若是第一个字母同样,那么比较第二个、第三个乃至后面的字母。若是比到最后两个单词不同长(好比,sigh 和 sight),那么把短者排在前。
所以,在 [1, 23, 2, 3].sort()
中,比较的时候实际上是 '1'、'23'、'2'、'3'、
来进行比较的(注意引号,是字符串)。 下面是这些字符串的 charCode
表:ide
1 | 23 | 2 | 3 |
---|---|---|---|
49 | 50 51 | 50 | 51 |
'1'
和 '23'
比较,先比较第一位 charCode
, 49 小于50,直接断定成功,'1'
<'23'
,而 '23'
和 '2'
比较,第一位 charCode
相同,判断不了,看第二位; 而第二位上 一个是 51
,一个是”空“ 的,所以 2 < 23。而'23'
和 '3'
比较, 因为2
的 charCode
小于 3
的, 所以也直接断定成功,23 < 3。ui
类比能够考虑一下咱们小学时候使用的字典 ,字典前面的拼音索引, a
i 的拼音也始终在 ba
前面,由于 a 的字典序小于 b
。
一样的,来看下英文: ['a','ba','b','c','ca']
。
a | b | ba | c | ca |
---|---|---|---|---|
97 | 98 | 98 97 | 99 | 97 |
根据上面的规则,咱们能够很容易推断出排序结果: ["a", "b", "ba", "c", "ca"]
最后看看中文的: ['啊','次','比例','中','毓','比侊']
啊 | 次 | 比例 | 中 | 毓 | 比侊 |
---|---|---|---|---|---|
21834 | 27425 | 27604 20363 | 20013 | 27603 | 27604 20362 |
一样的,根据上面的规则,结果也是一目了然:["中", "啊", "次", "毓", "比侊", "比例"]
。
中文的charCode
能够经过charCodeAt
来获取。
这样的排序结果不少时候并非咱们须要的,所以 sort
也容许咱们传入一个 compareFn
来实现自定义排序规则,经过返回 -一、0、1 来以为排序结果的大小顺序。对于能够比较的数值,咱们能够经过 x-y
的方式来处理,那么中文又该怎么办呢?能不能按照拼音的顺序来排序?
答案是确定的!
String 如今有了一个 localeCompare 的方法用于本地化比较。
['啊','次','比例','中','毓','比侊'].sort((x,y) => x.localeCompare(y)) // ["啊", "比侊", "比例", "次", "毓", "中"]
在 MDN 中,在涉及到大量比较的时候更推荐的是 Intl.Collator。 Intl 对象是 ECMAScript 国际化 API 的一个命名空间,它提供了精确的字符串对比,数字格式化,日期和时间格式化。
const collator = new Intl.Collator() ['啊','次','比例','中','毓','比侊'].sort(collator.compare) // ["啊", "比侊", "比例", "次", "毓", "中"]
固然 localeCompare
和 Intl.Collator
容许传入参数指定 locale
, 有兴趣的能够去 MDN 上看看用法。
最后,内置 sort
的默认 compareFn
方法是基于字典序排序的, 而字典序比较的对象是 数据的 charCode
。当 sort
默认的排序行为和预期不同或者没法知足需求的时候,咱们能够传入自定义的 compareFn
来进行排序。对于中文或者须要本地化比较的场景下,可使用 String.localeCompare
或者 Intl.Collator
来进行比较。
最后的最后,若有错误,欢迎你们指出,一块儿讨论进步。