V8 JavaScript中sort的实现

此文是学习过程分享,欢迎讨论,有任何问题欢迎私信

JS中sort函数的基本使用

Array.sort(compareFunctuion)

不传参数

  • 不传递参数时,调用Array.sort函数默认是升序排序
let a = [2,0,6,1,77,0,52,0,25,7]
a.sort()
console.log(a);

image.png
你会发现最终结果不是按着数字大小升序,因为如果不传递参数,sort函数会默认数组中的元素转换成字符串形式,以字符串的每个Unicode编码进行比较,所以并不是按着数字大小比较。

传递参数

sort函数传递function(x,y),通过比较x,y的大小来决定排序顺序

let a = [2,0,6,1,77,0,52,0,25,7]
a.sort(function(x,y){
return x-y   // x-y > 0 交换x,y的位置,采用从小打到大的顺序排序
})
console.log(a);

image.png

 let a = [2,0,6,1,77,0,52,0,25,7]
a.sort(function(x,y){
return y-x    // x-y < 0 即 y-x > 0 交换x,y两个的位置,按着降序排序 
})
console.log(a);

image.png

用于数组对象排序

sort的用途很广,往往在业务中,我们会收到来着后端的数据,但是我们需要将数据进一步排序处理,按着某种规则重新排序,我们就可以使用sort对数组对象排序。

let data = [
  {
    name:'李四',
    sno:'2021001',
    grade:89
  },
  {
    name:'王五',
    sno:'2021008',
    grade:8
  },
  {
    name:'周六',
    sno:'2021003',
    grade:99
  },
  {
    name:'王麻子',
    sno:'2021016',
    grade:23
  },
]
//    对学生的成绩进行排序
function rank_Rule(key){
  return function(x,y){

    let value1 = x[key] // 第一个元素
    let value2 = y[key] //第二个元素
    return value1 - value2 //升序排序
  }
}
data.sort(rank_Rule("grade"))
console.log('成绩升序排序:',data);

image.png

底层原理实现

V8 旧版本

什么是V8?

V8是Google的开源高性能JavaScript和WebAssembly引擎,用C++编写。它用于Chrome和Node.js等。它实现了 ECMAScript 和 WebAssembly,并在使用 x64、IA-32、ARM 或 MIPS 处理器的 Windows 7 或更高版本、macOS 10.12+ 和 Linux 系统上运行。V8 可以独立运行,也可以嵌入到任何 C++ 应用程序中。(摘自V8

现在网上大部分搜到的关于Array.sort的实现还是旧版本,即

  • 当 n<=10 时,采用插入排序;
  • 10<n <=1000,采用常规的快速排序
  • n > 1000,采用优化后的快速排序(每隔 200~215 个元素挑出一个元素,放到一个新数组中,然后对它排序,找到中间位置的数,以此作为中位数。)

部分源码:

function InnerArraySort(array, length, comparefn) {
  // In-place QuickSort algorithm.
  // For short (length <= 10) arrays, insertion sort is used for efficiency.

  if (!IS_CALLABLE(comparefn)) {
    comparefn = function (x, y) {
      if (x === y) return 0;
      if (%_IsSmi(x) && %_IsSmi(y)) {
        return %SmiLexicographicCompare(x, y);
      }
      x = TO_STRING(x);
      y = TO_STRING(y);
      if (x == y) return 0;
      else return x < y ? -1 : 1;
    };
  }
  function InsertionSort(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;
    }
  };

  function GetThirdIndex(a, from, to) {
    var t_array = new InternalArray();
    // Use both 'from' and 'to' to determine the pivot candidates.
    var increment = 200 + ((to - from) & 15);
    var j = 0;
    from += 1;
    to -= 1;
    for (var i = from; i < to; i += increment) {
      t_array[j] = [i, a[i]];
      j++;
    }
    t_array.sort(function(a, b) {
      return comparefn(a[1], b[1]);
    });
    var third_index = t_array[t_array.length >> 1][0];
    return third_index;
  }

  function QuickSort(a, from, to) {
    var third_index = 0;
    while (true) {
      // Insertion sort is faster for short arrays.
      if (to - from <= 10) {
        InsertionSort(a, from, to);
        return;
      }
      if (to - from > 1000) {
        third_index = GetThirdIndex(a, from, to);
      } else {
        third_index = from + ((to - from) >> 1);
      }
      // Find a pivot as the median of first, last and middle element.
      var v0 = a[from];
      var v1 = a[to - 1];
      var v2 = a[third_index];
      var c01 = comparefn(v0, v1);
      if (c01 > 0) {
        // v1 < v0, so swap them.
        var tmp = v0;
        v0 = v1;
        v1 = tmp;
      } // v0 <= v1.
      var c02 = comparefn(v0, v2);
      if (c02 >= 0) {
        // v2 <= v0 <= v1.
        var tmp = v0;
        v0 = v2;
        v2 = v1;
        v1 = tmp;
      } else {
        // v0 <= v1 && v0 < v2
        var c12 = comparefn(v1, v2);
        if (c12 > 0) {
          // v0 <= v2 < v1
          var tmp = v1;
          v1 = v2;
          v2 = tmp;
        }
      }
      // v0 <= v1 <= v2
      a[from] = v0;
      a[to - 1] = v2;
      var pivot = v1;
      var low_end = from + 1;   // Upper bound of elements lower than pivot.
      var high_start = to - 1;  // Lower bound of elements greater than pivot.
      a[third_index] = a[low_end];
      a[low_end] = pivot;

      // From low_end to i are elements equal to pivot.
      // From i to high_start are elements that haven't been compared yet.
      partition: for (var i = low_end + 1; i < high_start; i++) {
        var element = a[i];
        var order = comparefn(element, pivot);
        if (order < 0) {
          a[i] = a[low_end];
          a[low_end] = element;
          low_end++;
        } else if (order > 0) {
          do {
            high_start--;
            if (high_start == i) break partition;
            var top_elem = a[high_start];
            order = comparefn(top_elem, pivot);
          } while (order > 0);
          a[i] = a[high_start];
          a[high_start] = element;
          if (order < 0) {
            element = a[i];
            a[i] = a[low_end];
            a[low_end] = element;
            low_end++;
          }
        }
      }
      if (to - high_start < low_end - from) {
        QuickSort(a, high_start, to);
        to = low_end;
      } else {
        QuickSort(a, from, low_end);
        from = high_start;
      }
    }
  };

7.6版本后

在V8 7.6版本Array.sort方法不再是通过JavaScript来书写,而是采用了一种名为Torque的语言。
现在我们的浏览器几乎都不采用V8低版本的JavaScript,所以关于Array.sort方法早已经被重写。
我的chrome版本:(可以输入url:chrome://version查看)
image.png
Edge版本:
image.png

Torque是一种领域专用语言,具有类似TypeScript的语法,目前使用CSA作为其唯一的编译目标。Torque允许开发者使用与CSA几乎相同层次的流程控制操作,同时提供更高级别的构造,例如while和for循环。此外,它是强类型的,并且将来还会包含类似自动越界这样的安全检查,为V8引擎的工程师提供更强大的保障。

如果大家想了解关于Torque可以前往V8 Torque用户手册
在现在的V8引擎中,Array.sort采用了名为TimSort的方法实现,相比于之前的sort方法更加的稳定,快速
Time Sort源代码:

transitioning macro ArrayTimSortImpl(
  context: Context, sortState: SortState, length: Smi): void {
  if (length < 2) return;
let remaining: Smi = length;

// March over the array once, left to right, finding natural runs,
// and extending short natural runs to minrun elements.
let low: Smi = 0;
const minRunLength: Smi = ComputeMinRunLength(remaining);
while (remaining != 0) {
  let currentRunLength: Smi = CountAndMakeRun(low, low + remaining);

  // If the run is short, extend it to min(minRunLength, remaining).
  if (currentRunLength < minRunLength) {
    const forcedRunLength: Smi = SmiMin(minRunLength, remaining);
    BinaryInsertionSort(low, low + currentRunLength, low + forcedRunLength);
    currentRunLength = forcedRunLength;
  }

  // Push run onto pending-runs stack, and maybe merge.
  PushRun(sortState, low, currentRunLength);

  MergeCollapse(context, sortState);

  // Advance to find next run.
  low = low + currentRunLength;
  remaining = remaining - currentRunLength;
}

MergeForceCollapse(context, sortState);
dcheck(GetPendingRunsSize(sortState) == 1);
dcheck(GetPendingRunLength(sortState.pendingRuns, 0) == length);
}
  • 14
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值