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

瀏覽器相容性

另見