javascript中数组的sort()方法原理研究

今天又看了一遍Javascript高级程序设计,温习一下各引用类型的方法,看到数组这一块的sort()方法,不禁产生疑问:sort()方法要求传入一个参数,这个参数为一个比较函数,书籍中推荐的compare函数为

function compare(a, b) {
    return b - a;

};

按照我的理解,当对一个数组使用sort()方法时,会逐项地将数组中的元素传入到compare函数作为参数,打个比方,有个数组array = [1, 2],那么传入到compare中,就是a  = 1,b = 2,即compare返回值为2 - 1 = 1,最终就变成array.sort(1)。那么这样排序的结果是什么呢?来做个试验:

var array = [1, 2];

function compare(a, b) {

    return b - a;

}

console.log(array.sort(compare));

结果:[2, 1]


为何我传入sort的参数是1,结果却是从大到小排序了呢?

sort()方法的默认排序不是按照字符顺序从小到大么?

难不成sort()方法默认的参数是-1?

带着这个疑问继续做试验。PS:本篇博客试验用浏览器为Chrome53.0.2785.8,Firefox49.0.2,IE11.672.10586.0

首先扩充一下数组var array = [1, 20, 10, 2, 5];

不用写也知道array.sort() = [1, 10, 2, 20, 5],因为默认按字符顺序从小到大排序;

那么我们先来测试参数为-1,Chrome:array.sort(-1) = [1, 10, 2, 20, 5];Firefox和IE报错:参数错误。

接着我们测试参数为0,Chrome:array.sort(-1) = [1, 10, 2, 20, 5];Firefox和IE报错:参数错误。

再来测试参数为1,Chrome:array.sort(-1) = [1, 10, 2, 20, 5];Firefox和IE报错:参数错误。

结合W3C规范:

arrayObject.sort(sortby)
参数 描述
sortby 可选。规定排序顺序。必须是函数。


小结:W3C规定sort()方法的参数必须是函数,以上测试数值说明,chrome对传入数值只是不报错,但对结果没影响,而Firefox和IE则直接报错。


我们继续试验:

定义一个函数

function compare1() {

    return -1;

}

Chrome/Firefox:array.sort(compare1) = [1, 20, 10, 2, 5]

IE:array.sort(compare1) = [5, 2, 10, 20, 1]

我们把compare1()函数的返回值改为0:

Chrome/Firefox/IE:array.sort(compare1) = [1, 20, 10, 2, 5]

我们再把compare1()函数的返回值改为1:

Chrome/Firefox:array.sort(compare1) = [5, 2, 10, 20, 1]
IE:array.sort(compare1) = [1, 20, 10, 2, 5]


小结:不同浏览器对sort()方法的排序原理是不同的,这里可见,对于数值排序,在参数函数返回值为-1时,Chrome和Firefox什么都不做,,而IE则相当于执行了个reverse()。参数函数返回值为1时,上述浏览器的行为相反。参数函数返回值为0的情况各浏览器都什么也不做。


那么我们再来看看书中推荐的排序函数在sort()方法中是如何工作的,试验继续:

function compare2(a, b) {

    console.log(a, b, b - a);

    return b - a;

}

console.log(array.sort(compare2));

首先,最终结果array = [20, 10, 5, 2, 1],不同浏览器的行为一致。

但中间不断传递参数的过程却不一致,这里Chrome和Firefox相同,IE不同,与前面我们用compare1函数作为参数函数时的表现类似。

Chrome和Firofox在控制台打印出的结果如下:

1 20 19

1 10 9

20 10 -10

1 2 1

10 2 -8

1 5 4

2 5 3

10 5 -5

[20, 10, 5, 2, 1]

我们来解读一下这些数字(待排序的数组array = [1, 20, 10, 2, 5]):

第一行:把数组的前两个元素1、20依次传给compare()函数的a和b,返回值为19,>0,元素1和20位置互换,现在数组array = [20, 1, 10, 2, 5];

第二行:将新数组的第二个元素1和第三个元素10依次传给a和b,返回值为9,>0,元素1和10位置互换,现在数组array = [20, 10, 1, 2, 5];

第三行:这次不是继续向下对比了,而是将已经比过并换过位置的元素20和10相互比较,返回值为-10, <0,元素20和10位置保持不变;

第四行:将新数组的第三个元素1和第四个元素2依次传给a和b,返回值为1,>0,元素1和2位置互换,现在数组array = [20, 10, 2, 1, 5];

第五行:将最新换过位置的元素2与前面排好序的元素10对比,返回值-8,<0,元素10和2位置保持不变;

第六行:将新数组的第四个元素1和第五个元素5依次传给a和b,返回值为4,>0,元素1和5位置互换,现在数组array = [20, 10, 2, 5, 1];

第七行:将最新换过位置的元素5与前面排好序的元素2对比,返回值3,>0,元素5和2位置互换,现在数组array = [20, 10, 5, 2, 1];

第八行:将最新换过位置的元素5与前面排好序的元素10对比,返回值-5,<0,元素5和10位置保持不变;

排序结束,最终返回数组array = [20, 10, 5, 2, 1]。


小结一下上面的排序过程:

1、参数函数返回值若<0,则元素位置保持不变,参数函数返回值若>0,则元素位置互换,核心思想是冒泡排序;PS:参数函数返回值=0,即对比的两个元素相等,位置保持不变;

2、一旦发生过元素位置变化,优先对已排过序的元素继续排序,永远保证已经对比过的元素的顺序,这一条在实际程序中应该是开辟了一个新数组,用于保存已经冒泡排序过的元素。


将这个排序过程反映为代码我认为应该是这样的:

        Array.prototype.sort = function(sortby) {
            if (typeof sortby === 'function') {
                var newArray = [];
                var tmp = 0;
                for (var i = 0, len = array.length; i < len - 1; i++) {
                    if (sortby(array[i], array[i + 1]) > 0) {
                        tmp = array[i + 1];
                        array[i + 1] = array[i];
                        array[i] = tmp;
                        newArray.push(array[i]);
                        if (newArray.length > 1) {
                            for (var l = newArray.length, j = l - 1; j > 1 ; j--) {
                                    if (sortby(newArray[j - 1], newArray[j]) > 0) {
                                        tmp = newArray[j];
                                        newArray[j] = newArray[j - 1];
                                        newArray[j - 1] = tmp;
                                    } else {
                                        break;
                                    }
                            }
                        }
                    } else {
                        newArray.push(array[i]);
                    }
                }
                newArray.push(array[len - 1]);
                return newArray;
            }
        }

IE在控制台打印的结果不贴了,IE比较怪异,它在把array数组中的元素依次传入参数函数compare2中时,是把前一个元素作为b,后一个元素作为a,返回值<0互换,>0保持,这样传参顺序与chrome是相反的,返回值逻辑与chrome也是相反的,负负得正,这样最后得到的结果就与chrome一致了。


今天就先写到这里,以后再补充。


你可能感兴趣的:(javascript)