JS 中 Array.sort 的那些事儿

你们都知道 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 将比较的对象转化成字符串。算法

在转化成字符串这一步以后,坑就出现了。当 xy (这里 xy 都已是字符串了)进行比较的,实际上是依据字典序来比较的,而比较的对象其实数据的 charCodesegmentfault

字典序的比较方法引用一下 维基百科上的说法:浏览器

是先按照第一个字母、以 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' 比较, 因为2charCode 小于 3 的, 所以也直接断定成功,23 < 3。ui

类比能够考虑一下咱们小学时候使用的字典 ,字典前面的拼音索引, ai 的拼音也始终在 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.CollatorIntl 对象是 ECMAScript 国际化 API 的一个命名空间,它提供了精确的字符串对比,数字格式化,日期和时间格式化。

const collator = new Intl.Collator()
['啊','次','比例','中','毓','比侊'].sort(collator.compare)
// ["啊", "比侊", "比例", "次", "毓", "中"]

固然 localeCompareIntl.Collator 容许传入参数指定 locale, 有兴趣的能够去 MDN 上看看用法。

最后,内置 sort 的默认 compareFn 方法是基于字典序排序的, 而字典序比较的对象是 数据的 charCode。当 sort 默认的排序行为和预期不同或者没法知足需求的时候,咱们能够传入自定义的 compareFn 来进行排序。对于中文或者须要本地化比较的场景下,可使用 String.localeCompare 或者 Intl.Collator 来进行比较。

最后的最后,若有错误,欢迎你们指出,一块儿讨论进步。

相关文章
相关标签/搜索