今天又看了一遍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一致了。
今天就先写到这里,以后再补充。