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 來進行比較。

最後的最後,若有錯誤,歡迎你們指出,一塊兒討論進步。

相關文章
相關標籤/搜索