你們都知道 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
來進行比較。
最後的最後,若有錯誤,歡迎你們指出,一塊兒討論進步。