JavaScript中的Array.prototype.sort方法详解

前几天在某公司面试的时候被问到关于这个方法的默认值的问题(然而面试官跟我说的其实是错的,当场我还不够底气去反驳)。突然发现对这个方法的了解还不够,因此回来查了资料,看了v8引擎的实现和ECMA标准,在这分享一下我的总结。

先给几个结论把,然后我们再从ECMA标准去看这个方法。

  1. sort方法会修改原本数组。
  2. sort方法是不稳定的。
  3. sort方法可以接受一个可选的参数,comparefn(比较回调函数)。
  4. sort方法始终是默认升序的。

1.sort方法会修改原本数组

let a = [1, 20, 13, 110]
a.sort()
console.log(a) // 输出[1, 110, 13, 20]

如上,a在调用sort方法后,自身数组被修改。


2.sort方法是不稳定的

这句话具体来说应该是,sort方法在长数组排序的时候是不稳定的。在v8引擎的里,对于短数组会使用插入排序,而插入排序是稳定的。对于长数组会使用快速排序,而快速排序一般是不稳定的。具体可以读v8引擎的代码,v8引擎内的数组方法实现,从710行开始。


3.sort方法可以接受一个可选的参数,ccomparefn(比较回调函数)。

我们来看一下v8引擎中关于这一部分的代码

  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;
    };
  }

可以看出,在不传递comparefn这个参数的情况下,默认会使用转换为的字符串的诸个字符的Unicode位点进行排序。另外值得注意的一点是,undefined在默认情况下总是会被排在数组的最后,这一点可以参考ECMA-262标准中关于Array.prototype.sort(comparefn)的描述。

When the SortCompare operatoris called with two arguments x and y, the
following steps are taken:

  1. If x and y are both undefined, return +0.
  2. If x is undefined, return 1.
  3. If y is undefined, return −1.
  4. If the argument comparefn was not provided in the call to sort, go to step 7.
  5. Call comparefn with arguments x and y.
  6. Return Result(5).
  7. Call ToString(x).
  8. Call ToString(y).
  9. If Result(7) < Result(8), return −1.
  10. If Result(7) > Result(8), return 1.
  11. Return +0.

以下是我简单翻译的规则:

当比较操作函数用x,y两个参数调用的时候,会按照接下来的步骤进行:

  1. 如果x,y都是undefined,返回 +0
  2. 如果x是undefined,返回 1
  3. 如果y是undefined,返回 -1
  4. 如果没有提供comparefn,跳至第七步
  5. 用comparefn比较x,y
  6. 返回第五步的比较结果
  7. x.toString()
  8. y.toString()
  9. 如果第八步大于第七步,返回-1
  10. 如果第七步大于第八步,返回1
  11. 返回+0

因此[1, 20, 2, 10].sort()的结果会是[1, 10, 2, 20],而不是预期的[1, 2, 10, 20]。


4. sort方法始终是默认升序的。

另外关于如何根据x,y比较结果进行排序,有以下规则:

  • 如果 comparefn(a, b) 小于 0 ,那么 a 会被排列到 b 之前;
  • 如果 comparefn(a, b) 等于 0 , a 和 b 的相对位置不变。备注: ECMAScript 标准并不保证这一行为,而且也不是所有浏览器都会遵守(例如 Mozilla 在 2003 年之前的版本, 例如v8引擎, 这一点其实就是上面所说的排序不稳定);
  • 如果 comparefn(a, b) 大于 0 , b 会被排列到 a 之前。

所以sort方法返回的数组永远是该方法认为的升序数组。我们要做的事情就是令我们想要放在后面的数大于放在前面的数。

比如我们想要返回一个降序的数字数组传入的comparefn应该是 (a, b) => b - a

参考资料:

  1. MDN关于sort算法的文档
  2. v8引擎数组实现
  3. ECMA-262标准

你可能感兴趣的:(javascript)