Array.prototype.sort()

Baseline 已广泛支持

此特性已相当成熟,可在许多设备和浏览器版本上使用。自 ⁨2015 年 7 月⁩以来,各浏览器均已提供此特性。

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

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

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

试一试

const months = ["March", "Jan", "Feb", "Dec"];
months.sort();
console.log(months);
// Expected output: Array ["Dec", "Feb", "Jan", "March"]

const array = [1, 30, 4, 21, 100000];
array.sort();
console.log(array);
// Expected output: Array [1, 100000, 21, 30, 4]

语法

js
sort()
sort(compareFn)

参数

compareFn 可选

一个用于确定元素顺序的函数。该函数将使用以下参数进行调用:

a

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

b

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

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

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

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

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

返回值

对原始数组的引用,已排序。请注意,数组是原地排序的,不会创建副本。

描述

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

sort() 方法会保留空位。如果源数组是稀疏的,则空位会被移到数组的末尾,并且总是排在所有 undefined 之后。

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

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

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

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

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

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

  • 纯净:比较器不修改正在比较的对象或任何外部状态。(这很重要,因为无法保证比较器何时以及如何被调用,因此任何特定的调用都不应产生可见的外部影响。)
  • 稳定:比较器对同一输入对返回相同的结果。
  • 自反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 中减去 b。以下函数将按升序对数组进行排序(如果它不包含 NaN):

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

sort() 方法是通用的。它只期望 this 值具有 length 属性和整数键属性。虽然字符串也像数组一样,但此方法不适用于字符串,因为字符串是不可变的。

示例

创建、显示和排序数组

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

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']

排序对象数组

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

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 字符

对于带有非ASCII 字符(即带重音字符(e、é、è、a、ä 等)的字符串、非英语语言的字符串)的字符串排序,请使用 String.prototype.localeCompare()。此函数可以比较这些字符,使它们按正确的顺序排列。

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é']

使用 map 进行排序

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

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) => ({ 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 的开源库,它应用了这种方法。

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

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

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())一样,请使用 toSorted() 方法。或者,您可以在调用 sort() 之前进行浅拷贝,使用展开语法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

排序稳定性

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

例如,假设您有一份按姓名按字母顺序预先排序的学生名单。

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

将此数组按 grade 升序排序后

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

students 变量将具有以下值:

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)将保持调用排序之前的顺序。这就是稳定排序算法所保证的。

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

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

使用非良好定义的比较器进行排序

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

例如,考虑以下代码:

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]

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

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]

由于这种实现不一致性,因此始终建议您通过遵循五个约束来使比较器良好定义。

在稀疏数组上使用 sort()

空位会被移到数组的末尾。

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

在非数组对象上调用 sort()

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

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

规范

规范
ECMAScript® 2026 语言规范
# sec-array.prototype.sort

浏览器兼容性

另见