Array.prototype.sort()

Array 实例的 sort() 方法对数组 到位 的元素进行排序,并返回对同一数组(现已排序)的引用。默认排序顺序为升序,基于将元素转换为字符串,然后比较它们的 UTF-16 代码单元值序列。

¥The sort() method of Array instances sorts the elements of an array in place and returns the reference to the same array, now sorted. The default sort order is ascending, built upon converting the elements into strings, then comparing their sequences of UTF-16 code units values.

排序的时间和空间复杂度无法保证,因为它取决于实现。

¥The time and space complexity of the sort cannot be guaranteed as it depends on the implementation.

要对数组中的元素进行排序而不改变原始数组,请使用 toSorted()

¥To sort the elements in an array without mutating the original array, use toSorted().

Try it

语法

¥Syntax

js
sort()
sort(compareFn)

参数

¥Parameters

compareFn Optional

确定元素顺序的函数。使用以下参数调用该函数:

a

用于比较的第一个元素。永远不会是 undefined

b

用于比较的第二个元素。永远不会是 undefined

它应该返回一个数字,其中:

  • 负值表示 a 应位于 b 之前。
  • 正值表示 a 应位于 b 之后。
  • 零或 NaN 表示 ab 被视为相等。

为了记住这一点,请记住 (a, b) => a - b 按升序对数字进行排序。

如果省略,数组元素将转换为字符串,然后根据每个字符的 Unicode 代码点值进行排序。

返回值

¥Return value

对原始数组的引用,现已排序。请注意,该数组按 到位 排序,并且不进行任何复制。

¥The reference to the original array, now sorted. Note that the array is sorted in place, and no copy is made.

描述

¥Description

如果未提供 compareFn,则通过将所有非 undefined 数组元素转换为字符串并按 UTF-16 代码单元顺序比较字符串来对所有非 undefined 数组元素进行排序。例如,"banana" 在 "cherry" 之前。在数字排序中,9 排在 80 之前,但由于数字会转换为字符串,因此按照 Unicode 顺序,"80" 排在 "9" 之前。所有 undefined 元素都排序到数组末尾。

¥If compareFn is not supplied, all non-undefined array elements are sorted by converting them to strings and comparing strings in UTF-16 code units order. For example, "banana" comes before "cherry". In a numeric sort, 9 comes before 80, but because numbers are converted to strings, "80" comes before "9" in the Unicode order. All undefined elements are sorted to the end of the array.

sort() 方法保留空槽。如果源数组是 sparse,则空槽会移至数组末尾,并且始终位于所有 undefined 之后。

¥The sort() method preserves empty slots. If the source array is sparse, the empty slots are moved to the end of the array, and always come after all the undefined.

注意:在 UTF-16 中,\uFFFF 以上的 Unicode 字符被编码为范围 \uD800 的两个代理代码单元 - \uDFFF。每个代码单元的值被单独考虑以进行比较。因此,由代理对 \uD855\uDE51 形成的字符将被排序在字符 \uFF3A 之前。

¥Note: In UTF-16, Unicode characters above \uFFFF are encoded as two surrogate code units, of the range \uD800 - \uDFFF. The value of each code unit is taken separately into account for the comparison. Thus the character formed by the surrogate pair \uD855\uDE51 will be sorted before the character \uFF3A.

如果提供了 compareFn,则所有非 undefined 数组元素都根据比较函数的返回值进行排序(所有 undefined 元素都排序到数组末尾,不调用 compareFn)。

¥If compareFn is supplied, all non-undefined array elements are sorted according to the return value of the compare function (all undefined elements are sorted to the end of the array, with no call to compareFn).

compareFn(a, b) 返回值 排序
0 a 排序在 b 之后,例如 [b, a]
< 0 a 排序在 b 之前,例如 [a, b]
=== 0 保留 ab 的原始顺序

因此,比较函数具有以下形式:

¥So, the compare function has the following form:

js
function compareFn(a, b) {
  if (a is less than b by some ordering criterion) {
    return -1;
  } else if (a is greater than b by the ordering criterion) {
    return 1;
  }
  // a must be equal to b
  return 0;
}

更正式地说,比较器应具有以下属性,以确保正确的排序行为:

¥More formally, the comparator is expected to have the following properties, in order to ensure proper sort behavior:

  • 纯的:比较器不会改变被比较的对象或任何外部状态。(这很重要,因为无法保证何时以及如何调用比较器,因此任何特定的调用都不应该对外部产生可见的影响。)
  • 稳定的:比较器使用同一对输入返回相同的结果。
  • 反身性:compareFn(a, a) === 0
  • 反对称:compareFn(a, b)compareFn(b, a) 必须都是 0 或具有相反的符号。
  • 传递性:如果 compareFn(a, b)compareFn(b, c) 均为正、零或负,则 compareFn(a, c) 与前两者具有相同的正性。

符合上述约束的比较器将始终能够返回全部 10-1,或者始终返回 0。例如,如果比较器仅返回 10,或者仅返回 0-1,则由于反对称性被破坏,它将无法可靠地排序。始终返回 0 的比较器将导致数组根本不会更改,但仍然可靠。

¥A comparator conforming to the constraints above will always be able to return all of 1, 0, and -1, or consistently return 0. For example, if a comparator only returns 1 and 0, or only returns 0 and -1, it will not be able to sort reliably because anti-symmetry is broken. A comparator that always returns 0 will cause the array to not be changed at all, but is reliable nonetheless.

默认的字典比较器满足上述所有约束。

¥The default lexicographic comparator satisfies all constraints above.

要比较数字而不是字符串,比较函数可以从 a 中减去 b。以下函数将按升序对数组进行排序(如果不包含 NaN):

¥To compare numbers instead of strings, the compare function can subtract b from a. The following function will sort the array in ascending order (if it doesn't contain NaN):

js
function compareNumbers(a, b) {
  return a - b;
}

sort() 方法是 generic。它只期望 this 值具有 length 属性和整数键控属性。虽然字符串也是类似数组的,但此方法不适合应用于它们,因为字符串是不可变的。

¥The sort() method is generic. It only expects the this value to have a length property and integer-keyed properties. Although strings are also array-like, this method is not suitable to be applied on them, as strings are immutable.

示例

¥Examples

创建、显示和排序数组

¥Creating, displaying, and sorting an array

以下示例创建四个数组并显示原始数组,然后显示排序后的数组。数值数组在不使用比较函数的情况下进行排序,然后使用比较函数进行排序。

¥The following example creates four arrays and displays the original array, then the sorted arrays. The numeric arrays are sorted without a compare function, then sorted using one.

js
const stringArray = ["Blue", "Humpback", "Beluga"];
const numberArray = [40, 1, 5, 200];
const numericStringArray = ["80", "9", "700"];
const mixedNumericArray = ["80", "9", "700", 40, 1, 5, 200];

function compareNumbers(a, b) {
  return a - b;
}

stringArray.join(); // 'Blue,Humpback,Beluga'
stringArray.sort(); // ['Beluga', 'Blue', 'Humpback']

numberArray.join(); // '40,1,5,200'
numberArray.sort(); // [1, 200, 40, 5]
numberArray.sort(compareNumbers); // [1, 5, 40, 200]

numericStringArray.join(); // '80,9,700'
numericStringArray.sort(); // ['700', '80', '9']
numericStringArray.sort(compareNumbers); // ['9', '80', '700']

mixedNumericArray.join(); // '80,9,700,40,1,5,200'
mixedNumericArray.sort(); // [1, 200, 40, 5, '700', '80', '9']
mixedNumericArray.sort(compareNumbers); // [1, 5, '9', 40, '80', 200, '700']

对对象数组进行排序

¥Sorting array of objects

对象数组可以通过比较其属性之一的值来排序。

¥Arrays of objects can be sorted by comparing the value of one of their properties.

js
const items = [
  { name: "Edward", value: 21 },
  { name: "Sharpe", value: 37 },
  { name: "And", value: 45 },
  { name: "The", value: -12 },
  { name: "Magnetic", value: 13 },
  { name: "Zeros", value: 37 },
];

// sort by value
items.sort((a, b) => a.value - b.value);

// sort by name
items.sort((a, b) => {
  const nameA = a.name.toUpperCase(); // ignore upper and lowercase
  const nameB = b.name.toUpperCase(); // ignore upper and lowercase
  if (nameA < nameB) {
    return -1;
  }
  if (nameA > nameB) {
    return 1;
  }

  // names must be equal
  return 0;
});

对非 ASCII 字符进行排序

¥Sorting non-ASCII characters

要对包含非 ASCII 字符的字符串(即包含重音字符(e、é、è、a、ä 等)的字符串、英语以外的语言的字符串)进行排序,请使用 String.prototype.localeCompare()。此函数可以比较这些字符,以便它们以正确的顺序出现。

¥For sorting strings with non-ASCII characters, i.e. strings with accented characters (e, é, è, a, ä, etc.), strings from languages other than English, use String.prototype.localeCompare(). This function can compare those characters so they appear in the right order.

js
const items = ["réservé", "premier", "communiqué", "café", "adieu", "éclair"];
items.sort((a, b) => a.localeCompare(b));

// items is ['adieu', 'café', 'communiqué', 'éclair', 'premier', 'réservé']

按映射排序

¥Sorting with map

数组中的每个元素可以多次调用 compareFn。根据 compareFn 的性质,这可能会产生较高的开销。compareFn 做的工作越多,需要排序的元素越多,使用 map() 进行排序可能会更高效。这个想法是遍历数组一次,将用于排序的实际值提取到临时数组中,对临时数组进行排序,然后遍历临时数组以获得正确的顺序。

¥The compareFn can be invoked multiple times per element within the array. Depending on the compareFn's nature, this may yield a high overhead. The more work a compareFn does and the more elements there are to sort, it may be more efficient to use map() for sorting. The idea is to traverse the array once to extract the actual values used for sorting into a temporary array, sort the temporary array, and then traverse the temporary array to achieve the right order.

js
// the array to be sorted
const data = ["delta", "alpha", "charlie", "bravo"];

// temporary array holds objects with position and sort-value
const mapped = data.map((v, i) => {
  return { i, value: someSlowOperation(v) };
});

// sorting the mapped array containing the reduced values
mapped.sort((a, b) => {
  if (a.value > b.value) {
    return 1;
  }
  if (a.value < b.value) {
    return -1;
  }
  return 0;
});

const result = mapped.map((v) => data[v.i]);

有一个名为 mapsort 的开源库应用了这种方法。

¥There is an open source library available called mapsort which applies this approach.

sort() 返回对同一数组的引用

¥sort() returns the reference to the same array

sort() 方法返回对原始数组的引用,因此改变返回的数组也会改变原始数组。

¥The sort() method returns a reference to the original array, so mutating the returned array will mutate the original array as well.

js
const numbers = [3, 1, 4, 1, 5];
const sorted = numbers.sort((a, b) => a - b);
// numbers and sorted are both [1, 1, 3, 4, 5]
sorted[0] = 10;
console.log(numbers[0]); // 10

如果你希望 sort() 不改变原始数组,而是像其他数组方法(例如 map())一样返回 shallow-copied 数组,请使用 toSorted() 方法。或者,你可以在调用 sort() 之前使用 扩展语法Array.from() 执行浅复制。

¥In case you want sort() to not mutate the original array, but return a shallow-copied array like other array methods (e.g. map()) do, use the toSorted() method. Alternatively, you can do a shallow copy before calling sort(), using the spread syntax or Array.from().

js
const numbers = [3, 1, 4, 1, 5];
// [...numbers] creates a shallow copy, so sort() does not mutate the original
const sorted = [...numbers].sort((a, b) => a - b);
sorted[0] = 10;
console.log(numbers[0]); // 3

排序稳定性

¥Sort stability

从版本 10(或 ECMAScript 2019)开始,规范规定 Array.prototype.sort 是稳定的。

¥Since version 10 (or ECMAScript 2019), the specification dictates that Array.prototype.sort is stable.

例如,假设你有一份学生名单及其成绩。请注意,学生名单已按名称字母顺序预先排序:

¥For example, say you had a list of students alongside their grades. Note that the list of students is already pre-sorted by name in alphabetical order:

js
const students = [
  { name: "Alex", grade: 15 },
  { name: "Devlin", grade: 15 },
  { name: "Eagle", grade: 13 },
  { name: "Sam", grade: 14 },
];

grade 升序对该数组进行排序后:

¥After sorting this array by grade in ascending order:

js
students.sort((firstItem, secondItem) => firstItem.grade - secondItem.grade);

students 变量将具有以下值:

¥The students variable will then have the following value:

js
[
  { name: "Eagle", grade: 13 },
  { name: "Sam", grade: 14 },
  { name: "Alex", grade: 15 }, // original maintained for similar grade (stable sorting)
  { name: "Devlin", grade: 15 }, // original maintained for similar grade (stable sorting)
];

请务必注意,具有相同成绩的学生(例如 Alex 和 Devlin)将保持与调用排序之前相同的顺序。这就是稳定的排序算法所保证的。

¥It's important to note that students that have the same grade (for example, Alex and Devlin), will remain in the same order as before calling the sort. This is what a stable sorting algorithm guarantees.

在版本 10(或 ECMAScript 2019)之前,无法保证排序稳定性,这意味着你最终可能会得到以下结果:

¥Before version 10 (or ECMAScript 2019), sort stability was not guaranteed, meaning that you could end up with the following:

js
[
  { name: "Eagle", grade: 13 },
  { name: "Sam", grade: 14 },
  { name: "Devlin", grade: 15 }, // original order not maintained
  { name: "Alex", grade: 15 }, // original order not maintained
];

使用非格式良好的比较器进行排序

¥Sorting with non-well-formed comparator

如果比较函数不满足所有纯度、稳定性、自反性、反对称性和传递性规则(如 description 中所述),则程序的行为没有明确定义。

¥If a comparing function does not satisfy all of purity, stability, reflexivity, anti-symmetry, and transitivity rules, as explained in the description, the program's behavior is not well-defined.

例如,考虑以下代码:

¥For example, consider this code:

js
const arr = [3, 1, 4, 1, 5, 9];
const compareFn = (a, b) => (a > b ? 1 : 0);
arr.sort(compareFn);

这里的 compareFn 函数不是良式的,因为它不满足反对称性:如果是 a > b,则返回 1;但通过交换 ab,它返回 0 而不是负值。因此,不同引擎的结果数组会有所不同。例如,V8(Chrome、Node.js 等使用)和 JavaScriptCore(Safari 使用)根本不会对数组进行排序并返回 [3, 1, 4, 1, 5, 9],而 SpiderMonkey(Firefox 使用)将返回按升序排序的数组,如 [1, 1, 3, 4, 5, 9]

¥The compareFn function here is not well-formed, because it does not satisfy anti-symmetry: if a > b, it returns 1; but by swapping a and b, it returns 0 instead of a negative value. Therefore, the resulting array will be different across engines. For example, V8 (used by Chrome, Node.js, etc.) and JavaScriptCore (used by Safari) would not sort the array at all and return [3, 1, 4, 1, 5, 9], while SpiderMonkey (used by Firefox) will return the array sorted ascendingly, as [1, 1, 3, 4, 5, 9].

但是,如果稍微更改 compareFn 函数以使其返回 -10

¥However, if the compareFn function is changed slightly so that it returns -1 or 0:

js
const arr = [3, 1, 4, 1, 5, 9];
const compareFn = (a, b) => (a > b ? -1 : 0);
arr.sort(compareFn);

然后 V8 和 JavaScriptCore 将其降序排序,如 [9, 5, 4, 3, 1, 1],而 SpiderMonkey 按原样返回:[3, 1, 4, 1, 5, 9]

¥Then V8 and JavaScriptCore sorts it descendingly, as [9, 5, 4, 3, 1, 1], while SpiderMonkey returns it as-is: [3, 1, 4, 1, 5, 9].

由于这种实现不一致,我们始终建议你遵循五个约束来使比较器格式良好。

¥Due to this implementation inconsistency, you are always advised to make your comparator well-formed by following the five constraints.

在稀疏数组上使用 sort()

¥Using sort() on sparse arrays

空槽被移动到数组的末尾。

¥Empty slots are moved to the end of the array.

js
console.log(["a", "c", , "b"].sort()); // ['a', 'b', 'c', empty]
console.log([, undefined, "a", "b"].sort()); // ["a", "b", undefined, empty]

对非数组对象调用 sort()

¥Calling sort() on non-array objects

sort() 方法读取 thislength 属性。然后,它收集 0length - 1 范围内的所有现有整数键控属性,对它们进行排序,然后将它们写回。如果范围内缺少属性,则相应的尾随属性为 deleted,就好像不存在的属性被排序到末尾一样。

¥The sort() method reads the length property of this. It then collects all existing integer-keyed properties in the range of 0 to length - 1, sorts them, and writes them back. If there are missing properties in the range, the corresponding trailing properties are deleted, as if the non-existent properties are sorted towards the end.

js
const arrayLike = {
  length: 3,
  unrelated: "foo",
  0: 5,
  2: 4,
};
console.log(Array.prototype.sort.call(arrayLike));
// { '0': 4, '1': 5, length: 3, unrelated: 'foo' }

规范

Specification
ECMAScript Language Specification
# sec-array.prototype.sort

¥Specifications

浏览器兼容性

BCD tables only load in the browser

¥Browser compatibility

也可以看看