你应该知道的JS(数组)-Array.sort

1,593 阅读2分钟

上一篇(Array.from)juejin.cn/post/686967…

基础语法

_定义:_sort() 方法用于对数组的元素进行排序。

语法:array.sort(sortfunction)

参数:

参数描述
sortfunction
可选。规定排序顺序。必须是函数。

返回值:

Type描述
Array对数组的引用。请注意,数组在原数组上进行排序,不生成副本。

示例用法

1. 不传参

let arr1=[3,2,1];
arr1.sort()                                                // [1, 2, 3]

let arr2=[30,20,100];
arr2.sort()                                                // [100, 20, 30] ???

奇怪不?100在20前面。这不是出错了,而是因为sort方法在你不传参数时会给你一个默认参数(下图为v8引擎源码的sort代码,comparefn为sort方法参数)。默认comparefn方法会把数组内容转为string类型(下图720,721行)。

在js中,string类型进行比较时会依次比较两个字符串对应的字符编码值。字符串“100”和“20”比较时会先比较“1”和“2”,1<2,所以结果就是字符串“100”小于字符串“20”。这里我想起来去年一个朋友问我的问题。他碰到一个单看代码貌似没有错误的bug,这个bug导致他在计算一些优惠券数量时发生类似10<9的意外情况,但是完全找不到问题在哪。我听完之后第一反应就是这个问题导致的,于是在测试字符串比较情况后,发给他测试代码。他debugger之后发现果然是数据类型问题。关于关系操作符我也有很多想聊的,但是今天毕竟是说数组的sort方法,在此不做展开。

2. 传参

排序函数sortfunction规则:

  1. 传两个形参

  2. 当返回值为正数时,交换传入两形参在数组中位置

    let testArr=[1,3,2,4] testArr.sort((a,b)=>{ console.log(a,b) }) // 3 1 // 2 3 // 4 2

这里发现两个参数,前面的参数a实际上传入的是数组的后一项,与我们的直觉不同。很奇怪,那我们再看一下v8的数组源码

//这里是v8的sort核心排序的一部分,sort核心是快排。但是当快排的片段长度(to-form<10)会调用下面
//的插入排序方法。
var InsertionSort = function InsertionSort(a, from, to) {//a:待处理数组,from,to:处理片段范围
    for (var i = from + 1; i < to; i++) {
        var element = a[i];                              
        for (var j = i - 1; j >= from; j--) {
            var tmp = a[j];
            var order = comparefn(tmp, element);
            if (order > 0) {
                a[j + 1] = tmp;
            } else {
                break
            }
        }
        a[j + 1] = element;
    }
}

看起来又没有问题。

//规则测试
let arr=[1,3,2,4]
arr.sort((a,b)=>0)                                          // [1, 3, 2, 4] 不变
arr.sort((a,b)=>false)                                      // [1, 3, 2, 4] 不变
arr.sort((a,b)=>1)                                          // [1, 3, 2, 4] 不变
arr.sort((a,b)=>-1)                                         // [4, 2, 3, 1] 交换位置
这里又很奇怪结果为正数不变,负数反而交换位置,与各网站的sort定义及源码都不一样。这个结果与定义
相反加上参数ab顺序也与定义相反。负负得正,反而正常了。神奇!测试了半天确定我都没有弄错,
希望有知道的同学能够评论一下给我解惑。感谢!

//常用方式
let testArr=[1,3,2,4]
testArr.sort((a,b)=>a-b)                                    // [1, 2, 3, 4] 升序
testArr.sort((a,b)=>b-a)                                    // [4, 3, 2, 1] 降序
//对象数组
let objArr=[{age:25,name:'lgc'},{age:18,name:'loli'},{age:35,name:'uncle'}]
objArr.sort((a,b)=>a.age-b.age)                             // [{age:18,name:'loli'},                                                            //  {age:25,name:'lgc'},                                                            //  {age:35,name:'uncle'}]

总结及注意点:

1. sort方法会改变原始数组

2. 不传参时,数组的项会被转为字符串再进行比较

3. 排序函数每次传入数组的两个项进行比较,返回值为正数(存疑)时,交换两项的位置。其他情况不变。