Array
).length
for-of
和陣列 [ES6]
for-of
:遍歷元素for-of
:遍歷索引for-of
:遍歷 [索引,元素] 對...
) 將可迭代物件轉換為陣列Array.from()
將可迭代物件和類陣列物件轉換為陣列.find()
、.map()
、.filter()
等)
.find()
、.findIndex()
.map()
:複製並同時賦予元素新值.flatMap()
:對應到零個或多個值.filter()
:僅保留部分元素.reduce()
:從陣列中推導出一個值(進階).sort()
:排序陣列
Array
new Array()
Array
的靜態方法Array.prototype
的方法JavaScript 陣列是一種非常靈活的資料結構,可用作清單、堆疊、佇列、元組(例如成對)等。
有些與陣列相關的操作會對陣列造成破壞性的變更。其他操作則是非破壞性的,會產生新的陣列,並將變更套用至原始內容的副本。
建立陣列、讀取和寫入元素
// Creating an Array
const arr = ['a', 'b', 'c']; // Array literal
.deepEqual(
assert,
arr// Array literal
[ 'a',
'b',
'c', // trailing commas are ignored
];
)
// Reading elements
.equal(
assert0], 'a' // negative indices don’t work
arr[;
).equal(
assert.at(-1), 'c' // negative indices work
arr;
)
// Writing an element
0] = 'x';
arr[.deepEqual(
assert, ['x', 'b', 'c']
arr; )
陣列的長度
const arr = ['a', 'b', 'c'];
.equal(
assert.length, 3 // number of elements
arr;
).length = 1; // removing elements
arr.deepEqual(
assert, ['a']
arr;
).length] = 'b'; // adding an element
arr[arr.deepEqual(
assert, ['a', 'b']
arr; )
透過 .push()
具破壞性地新增元素
const arr = ['a', 'b'];
.push('c'); // adding an element
arr.deepEqual(
assert, ['a', 'b', 'c']
arr;
)
// Pushing Arrays (used as arguments via spreading (...)):
.push(...['d', 'e']);
arr.deepEqual(
assert, ['a', 'b', 'c', 'd', 'e']
arr; )
透過展開(...
)非破壞性地新增元素
const arr1 = ['a', 'b'];
const arr2 = ['c'];
.deepEqual(
assert...arr1, ...arr2, 'd', 'e'],
['a', 'b', 'c', 'd', 'e']
[; )
清除陣列(移除所有元素)
// Destructive – affects everyone referring to the Array:
const arr1 = ['a', 'b', 'c'];
.length = 0;
arr1.deepEqual(
assert, []
arr1;
)
// Non-destructive – does not affect others referring to the Array:
let arr2 = ['a', 'b', 'c'];
= [];
arr2 .deepEqual(
assert, []
arr2; )
迴圈遍歷元素
const arr = ['a', 'b', 'c'];
for (const value of arr) {
console.log(value);
}
// Output:
// 'a'
// 'b'
// 'c'
迴圈處理索引值對
const arr = ['a', 'b', 'c'];
for (const [index, value] of arr.entries()) {
console.log(index, value);
}
// Output:
// 0, 'a'
// 1, 'b'
// 2, 'c'
建立並填入陣列,當我們無法使用陣列文字時(例如因為我們不知道它們的長度或它們太大)
const four = 4;
// Empty Array that we’ll fill later
.deepEqual(
assertnew Array(four),
, , , ,] // four holes; last comma is ignored
[ ;
)
// An Array filled with a primitive value
.deepEqual(
assertnew Array(four).fill(0),
0, 0, 0, 0]
[;
)
// An Array filled with objects
// Why not .fill()? We’d get single object, shared multiple times.
.deepEqual(
assertArray.from({length: four}, () => ({})),
, {}, {}, {}]
[{};
)
// A range of integers
.deepEqual(
assertArray.from({length: four}, (_, i) => i),
0, 1, 2, 3]
[; )
本節簡要概述陣列 API。在本章的最後面有一個 更全面的快速參考。
從現有陣列衍生新陣列
> ['■','●','▲'].slice(1, 3)['●','▲']
> ['■','●','■'].filter(x => x==='■') ['■','■']
> ['▲','●'].map(x => x+x)['▲▲','●●']
> ['▲','●'].flatMap(x => [x,x])['▲','▲','●','●']
移除陣列中特定索引的元素
// .filter(): remove non-destructively
const arr1 = ['■','●','▲'];
.deepEqual(
assert.filter((_, index) => index !== 1),
arr1'■','▲']
[;
).deepEqual(
assert, ['■','●','▲'] // unchanged
arr1;
)
// .splice(): remove destructively
const arr2 = ['■','●','▲'];
.splice(1, 1); // start at 1, delete 1 element
arr2.deepEqual(
assert, ['■','▲'] // changed
arr2; )
計算陣列的摘要
> ['■','●','▲'].some(x => x==='●')true
> ['■','●','▲'].every(x => x==='●')false
> ['■','●','▲'].join('-')'■-●-▲'
> ['■','▲'].reduce((result,x) => result+x, '●')'●■▲'
> ['■','▲'].reduceRight((result,x) => result+x, '●')'●▲■'
反轉和填入
// .reverse() changes and returns `arr`
const arr = ['■','●','▲'];
.deepEqual(
assert.reverse(), arr
arr;
)// `arr` was changed:
.deepEqual(
assert, ['▲','●','■']
arr;
)
// .fill() works the same way:
.deepEqual(
assert'■','●','▲'].fill('●'),
['●','●','●']
[; )
.sort()
也會修改陣列並傳回
// By default, string representations of the Array elements
// are sorted lexicographically:
.deepEqual(
assert200, 3, 10].sort(),
[10, 200, 3]
[;
)
// Sorting can be customized via a callback:
.deepEqual(
assert200, 3, 10].sort((a,b) => a - b), // sort numerically
[3, 10, 200 ]
[ ; )
尋找陣列元素
> ['■','●','■'].includes('■')true
> ['■','●','■'].indexOf('■')0
> ['■','●','■'].lastIndexOf('■')2
> ['■','●','■'].find(x => x==='■')'■'
> ['■','●','■'].findIndex(x => x==='■')0
在開頭或結尾新增或移除元素
// Adding and removing at the start
const arr1 = ['■','●'];
.unshift('▲');
arr1.deepEqual(
assert, ['▲','■','●']
arr1;
).shift();
arr1.deepEqual(
assert, ['■','●']
arr1;
)
// Adding and removing at the end
const arr2 = ['■','●'];
.push('▲');
arr2.deepEqual(
assert, ['■','●','▲']
arr2;
).pop();
arr2.deepEqual(
assert, ['■','●']
arr2; )
在 JavaScript 中有兩種使用陣列的方式
在實務上,這兩種方式通常會混合使用。
值得注意的是,序列陣列非常靈活,我們可以使用它們作為(傳統的)陣列、堆疊和佇列。我們稍後會看到。
建立陣列的最佳方式是透過陣列文字
const arr = ['a', 'b', 'c'];
陣列文字以方括號 []
開頭和結尾。它建立一個具有三個元素的陣列:'a'
、'b'
和 'c'
。
陣列文字中允許尾隨逗號並忽略它們
const arr = [
'a',
'b',
'c',
; ]
要讀取陣列元素,我們在方括號中放入一個索引(索引從 0 開始)
const arr = ['a', 'b', 'c'];
.equal(arr[0], 'a'); assert
要變更陣列元素,我們指定一個具有索引的陣列
const arr = ['a', 'b', 'c'];
0] = 'x';
arr[.deepEqual(arr, ['x', 'b', 'c']); assert
陣列索引的範圍為 32 位元(不包括最大長度):[0, 232−1)
.length
每個陣列都有屬性 .length
,可用於讀取和變更(!)陣列中的元素數量。
陣列的長度永遠是最高索引加一
> const arr = ['a', 'b'];
> arr.length2
如果我們寫入陣列的長度索引,我們會附加一個元素
> arr[arr.length] = 'c';
> arr[ 'a', 'b', 'c' ]
> arr.length3
透過陣列方法 .push()
附加元素的另一種方式(具破壞性)
> arr.push('d');
> arr[ 'a', 'b', 'c', 'd' ]
如果我們設定 .length
,我們會透過移除元素來修剪陣列
> arr.length = 1;
> arr[ 'a' ]
練習:透過
.push()
移除空行
exercises/arrays/remove_empty_lines_push_test.mjs
多種陣列方法支援負數索引。如果索引為負數,它會加到陣列長度以產生可用的索引。因此,下列兩個 .slice()
呼叫等效:它們都從最後一個元素開始複製 arr
。
> const arr = ['a', 'b', 'c'];
> arr.slice(-1)[ 'c' ]
> arr.slice(arr.length - 1)[ 'c' ]
.at()
:讀取單一元素(支援負數索引)[ES2022]陣列方法 .at()
會傳回指定索引的元素。它支援正數和負數索引(-1
參照最後一個元素,-2
參照倒數第二個元素,依此類推)
> ['a', 'b', 'c'].at(0)'a'
> ['a', 'b', 'c'].at(-1)'c'
相反地,方括號運算子 []
不支援負數索引(且無法變更,因為這會損壞現有的程式碼)。它會將它們解釋為非元素屬性的鍵
const arr = ['a', 'b', 'c'];
-1] = 'non-element property';
arr[// The Array elements didn’t change:
.deepEqual(
assertArray.from(arr), // copy just the Array elements
'a', 'b', 'c']
[;
)
.equal(
assert-1], 'non-element property'
arr[; )
要清除(清空)陣列,我們可以將其 .length
設定為零
const arr = ['a', 'b', 'c'];
.length = 0;
arr.deepEqual(arr, []); assert
或者我們可以將新的空陣列指定給儲存陣列的變數
let arr = ['a', 'b', 'c'];
= [];
arr .deepEqual(arr, []); assert
後一種方法的優點是不會影響指向相同陣列的其他位置。然而,如果我們確實想要為所有人重設共用陣列,則需要前一種方法。
在陣列文字中,擴散元素包含三個點(...
),後面接一個表示式。它會導致表示式經過評估,然後進行反覆運算。每個反覆運算的值都會變成額外的陣列元素,例如
> const iterable = ['b', 'c'];
> ['a', ...iterable, 'd'][ 'a', 'b', 'c', 'd' ]
這表示我們可以使用擴散來建立陣列的副本,並將可反覆運算的項目轉換為陣列
const original = ['a', 'b', 'c'];
const copy = [...original];
const iterable = original.keys();
.deepEqual(
assert...iterable], [0, 1, 2]
[; )
然而,對於前兩個使用案例,我認為 Array.from()
更具自述性,而且較喜歡它
const copy2 = Array.from(original);
.deepEqual(
assertArray.from(original.keys()), [0, 1, 2]
; )
擴散也很方便用於將陣列(和其他可反覆運算的項目)串接成陣列
const arr1 = ['a', 'b'];
const arr2 = ['c', 'd'];
const concatenated = [...arr1, ...arr2, 'e'];
.deepEqual(
assert,
concatenated'a', 'b', 'c', 'd', 'e']); [
由於擴散使用反覆運算,因此它僅在值可反覆運算時有效
> [...'abc'] // strings are iterable[ 'a', 'b', 'c' ]
> [...123]TypeError: 123 is not iterable
> [...undefined]TypeError: undefined is not iterable
擴散和
Array.from()
會產生淺層副本
透過擴散或透過 Array.from()
複製陣列是淺層的:我們會在新的陣列中取得新的項目,但值會與原始陣列共用。淺層複製的後果會在 §28.4「擴散到物件文字 (...
) [ES2018]」 中示範。
方法 .keys()
列出陣列的索引
const arr = ['a', 'b'];
.deepEqual(
assertArray.from(arr.keys()), // (A)
0, 1]); [
.keys()
回傳一個可疊代的物件。在 A 行中,我們將該可疊代物件轉換為陣列。
列出陣列索引與列出屬性不同。前者產生數字;後者產生字串化的數字(除了非索引屬性金鑰之外)
const arr = ['a', 'b'];
.prop = true;
arr
.deepEqual(
assertObject.keys(arr),
'0', '1', 'prop']); [
方法 .entries()
將陣列的內容列為 [index, element] 對
const arr = ['a', 'b'];
.deepEqual(
assertArray.from(arr.entries()),
0, 'a'], [1, 'b']]); [[
以下是檢查值是否為陣列的兩種方法
> [] instanceof Arraytrue
> Array.isArray([])true
instanceof
通常很好用。如果值可能來自另一個領域,我們需要 Array.isArray()
。粗略來說,領域是 JavaScript 全域範圍的執行個體。有些領域彼此隔離(例如,瀏覽器中的 Web Workers),但也有我們可以在其中移動資料的領域,例如,瀏覽器中的同源 iframe。x instanceof Array
會檢查 x
的原型鏈,因此如果 x
是來自另一個領域的陣列,它會回傳 false
。
typeof
將陣列分類為物件
> typeof []'object'
for-of
和陣列 [ES6]我們已經在本書籍的前面章節中遇到 for-of
迴圈。本節將簡要回顧如何將它用於陣列。
for-of
:疊代元素以下 for-of
迴圈會疊代陣列中的元素
for (const element of ['a', 'b']) {
console.log(element);
}// Output:
// 'a'
// 'b'
for-of
:疊代索引此 for-of
迴圈會疊代陣列中的索引
for (const element of ['a', 'b'].keys()) {
console.log(element);
}// Output:
// 0
// 1
for-of
:疊代 [index, element] 對以下 for-of
迴圈會疊代 [index, element] 對。解構(稍後說明),為我們提供了在 for-of
的開頭設定 index
和 element
的便利語法。
for (const [index, element] of ['a', 'b'].entries()) {
console.log(index, element);
}// Output:
// 0, 'a'
// 1, 'b'
某些與陣列一起運作的運算只要最基本的東西:值必須是類陣列。類陣列值是一個具有以下屬性的物件
.length
:持有類陣列物件的長度。[0]
:持有索引 0 處的元素(依此類推)。請注意,如果我們使用數字作為屬性名稱,它們總是會強制轉換為字串。因此,[0]
會擷取金鑰為 '0'
的屬性的值。例如,Array.from()
接受類陣列物件並將它們轉換為陣列
// If we omit .length, it is interpreted as 0
.deepEqual(
assertArray.from({}),
;
[])
.deepEqual(
assertArray.from({length:2, 0:'a', 1:'b'}),
'a', 'b' ]); [
類陣列物件的 TypeScript 介面為
interface ArrayLike<T> {
: number;
length: number]: T;
[n }
類陣列物件在現代 JavaScript 中相對罕見
在 ES6 之前,類陣列物件很常見;現在我們不太常看到它們。
有兩種常見的方法將可迭代物件和類陣列值轉換為陣列
Array.from()
我比較喜歡後者,我覺得它比較不言自明。
...
) 將可迭代物件轉換為陣列在陣列字面中,透過 ...
擴散會將任何可迭代物件轉換為一系列陣列元素。例如
// Get an Array-like collection from a web browser’s DOM
const domCollection = document.querySelectorAll('a');
// Alas, the collection is missing many Array methods
.equal('map' in domCollection, false);
assert
// Solution: convert it to an Array
const arr = [...domCollection];
.deepEqual(
assert.map(x => x.href),
arr'https://2ality.com', 'https://exploringjs.dev.org.tw']); [
轉換之所以可行,是因為 DOM 集合是可迭代的。
Array.from()
將可迭代物件和類陣列物件轉換為陣列Array.from()
可用於兩種模式。
Array.from()
的模式 1:轉換第一種模式具有下列類型簽章
.from<T>(iterable: Iterable<T> | ArrayLike<T>): T[]
介面 Iterable
顯示在 同步迭代章節 中。介面 ArrayLike
出現在 本章節前面。
使用單一參數時,Array.from()
會將任何可迭代或類陣列物件轉換為陣列
> Array.from(new Set(['a', 'b']))[ 'a', 'b' ]
> Array.from({length: 2, 0:'a', 1:'b'})[ 'a', 'b' ]
Array.from()
的模式 2:轉換和對應Array.from()
的第二種模式包含兩個參數
.from<T, U>(
: Iterable<T> | ArrayLike<T>,
iterable: (v: T, i: number) => U,
mapFunc?: any)
thisArg: U[]
在此模式中,Array.from()
會執行多件事
iterable
。mapFunc
。選用參數 thisArg
會為 mapFunc
指定 this
。mapFunc
套用至每個迭代值。換句話說:我們從具有 T
類型元素的可迭代物件轉換為具有 U
類型元素的陣列。
以下是一個範例
> Array.from(new Set(['a', 'b']), x => x + x)[ 'aa', 'bb' ]
建立陣列的最佳方式是透過陣列字面。不過,我們無法總是使用陣列字面:陣列可能太大,我們可能在開發過程中不知道它的長度,或者我們可能想要保持它的長度彈性。因此,我建議使用下列技術來建立(並可能填入)陣列。
> new Array(3)[ , , ,]
請注意,結果有三個 空洞(空槽),陣列字面中的最後一個逗號總是會被忽略。
> new Array(3).fill(0)[0, 0, 0]
注意事項:如果我們對物件使用 .fill()
,則每個陣列元素都會參考這個物件(共用它)。
const arr = new Array(3).fill({});
0].prop = true;
arr[.deepEqual(
assert, [
arrprop: true},
{prop: true},
{prop: true},
{; ])
下一個小節說明如何修正這個問題。
> new Array(3).fill(0)[0, 0, 0]
對於大型陣列,暫時的陣列可能會消耗相當多的記憶體。下列方法沒有這個缺點,但較不具自述性
> Array.from({length: 3}, () => ({}))[{}, {}, {}]
我們使用暫時的 類陣列物件,而不是暫時的陣列。
function createRange(start, end) {
return Array.from({length: end-start}, (_, i) => i+start);
}.deepEqual(
assertcreateRange(2, 5),
2, 3, 4]); [
以下是建立從零開始的整數範圍的替代方法,有點駭客手法
/** Returns an iterable */
function createRange(end) {
return new Array(end).keys();
}.deepEqual(
assertArray.from(createRange(4)),
0, 1, 2, 3]); [
這會奏效,因為 .keys()
會將 洞 視為 undefined
元素,並列出其索引。
在處理整數或浮點數陣列時,我們應該考慮 型化陣列,這是為此目的而建立的。
JavaScript 沒有真正的多維陣列;我們需要使用元素為陣列的陣列
function initMultiArray(...dimensions) {
function initMultiArrayRec(dimIndex) {
if (dimIndex >= dimensions.length) {
return 0;
else {
} const dim = dimensions[dimIndex];
const arr = [];
for (let i=0; i<dim; i++) {
.push(initMultiArrayRec(dimIndex+1));
arr
}return arr;
}
}return initMultiArrayRec(0);
}
const arr = initMultiArray(4, 3, 2);
3][2][1] = 'X'; // last in each dimension
arr[.deepEqual(arr, [
assert0, 0 ], [ 0, 0 ], [ 0, 0 ] ],
[ [ 0, 0 ], [ 0, 0 ], [ 0, 0 ] ],
[ [ 0, 0 ], [ 0, 0 ], [ 0, 0 ] ],
[ [ 0, 0 ], [ 0, 0 ], [ 0, 'X' ] ],
[ [ ; ])
在本節中,我們將探討在使用陣列時不常遇到的現象。
您可能會認為陣列元素很特別,因為我們透過數字存取它們。但是,用於執行此操作的中括號運算子 []
與用於存取屬性的運算子相同。它會將任何值(不是符號)強制轉換為字串。因此,陣列元素是(幾乎)正常的屬性(A 行),而且我們使用數字或字串作為索引並無所謂(B 行和 C 行)
const arr = ['a', 'b'];
.prop = 123;
arr.deepEqual(
assertObject.keys(arr),
'0', '1', 'prop']); // (A)
[
.equal(arr[0], 'a'); // (B)
assert.equal(arr['0'], 'a'); // (C) assert
更令人困惑的是,這只是語言規範定義事物的方式(如果您願意,這是 JavaScript 的理論)。大多數 JavaScript 引擎會在後台最佳化,並使用實際整數來存取陣列元素(如果您願意,這是 JavaScript 的實務)。
用於陣列元素的屬性金鑰(字串!)稱為 索引。字串 str
是索引,如果將其轉換為 32 位元無符號整數再轉換回來,就會產生原始值。寫成公式如下
ToString(ToUint32(str)) === str
列出屬性金鑰時,索引會受到特別處理,它們總是會先出現,並像數字一樣排序('2'
出現在 '10'
之前)
const arr = [];
.prop = true;
arr1] = 'b';
arr[0] = 'a';
arr[
.deepEqual(
assertObject.keys(arr),
'0', '1', 'prop']); [
請注意,.length
、.entries()
和 .keys()
會將陣列索引視為數字,並忽略非索引屬性
.equal(arr.length, 2);
assert.deepEqual(
assertArray.from(arr.keys()), [0, 1]);
.deepEqual(
assertArray.from(arr.entries()), [[0, 'a'], [1, 'b']]);
我們使用 Array.from()
將 .keys()
和 .entries()
傳回的迭代物件轉換為陣列。
我們在 JavaScript 中區分兩種陣列
arr
中所有索引 i
(其中 0 ≤ i
< arr.length
)都存在,則此陣列為密集陣列。也就是說,這些索引形成一個連續的範圍。陣列在 JavaScript 中可以是稀疏的,因為陣列實際上是從索引到值的字典。
建議:避免孔洞
到目前為止,我們只看過密集陣列,建議避免孔洞:它們使我們的程式碼更複雜,而且陣列方法無法一致地處理它們。此外,JavaScript 引擎會最佳化密集陣列,使它們執行得更快。
我們可以在指定元素時略過索引來建立孔洞
const arr = [];
0] = 'a';
arr[2] = 'c';
arr[
.deepEqual(Object.keys(arr), ['0', '2']); // (A)
assert
.equal(0 in arr, true); // element
assert.equal(1 in arr, false); // hole assert
在 A 行中,我們使用 Object.keys()
,因為 arr.keys()
會將孔洞視為 undefined
元素,不會顯示它們。
建立孔洞的另一種方法是在陣列文字中略過元素
const arr = ['a', , 'c'];
.deepEqual(Object.keys(arr), ['0', '2']); assert
我們也可以刪除陣列元素
const arr = ['a', 'b', 'c'];
.deepEqual(Object.keys(arr), ['0', '1', '2']);
assertdelete arr[1];
.deepEqual(Object.keys(arr), ['0', '2']); assert
唉,陣列操作處理孔洞的方式有很多種。
有些陣列操作會移除孔洞
> ['a',,'b'].filter(x => true)[ 'a', 'b' ]
有些陣列操作會忽略孔洞
> ['a', ,'a'].every(x => x === 'a')true
有些陣列操作會忽略但保留孔洞
> ['a',,'b'].map(x => 'c')[ 'c', , 'c' ]
有些陣列操作會將孔洞視為 undefined
元素
> Array.from(['a',,'b'], x => x)[ 'a', undefined, 'b' ]
> Array.from(['a',,'b'].entries())[[0, 'a'], [1, undefined], [2, 'b']]
Object.keys()
的運作方式與 .keys()
不同(字串與數字,孔洞沒有金鑰)
> Array.from(['a',,'b'].keys())[ 0, 1, 2 ]
> Object.keys(['a',,'b'])[ '0', '2' ]
這裡沒有規則可以記住。如果陣列操作如何處理孔洞很重要,最好的方法是在主控台中進行快速測試。
JavaScript 的 Array
非常靈活,更像是陣列、堆疊和佇列的組合。本節探討新增和移除陣列元素的方法。大多數運算都可以以破壞性(修改陣列)和非破壞性(產生修改後的副本)兩種方式執行。
在以下程式碼中,我們以破壞性方式將單一元素預置到 arr1
,並將陣列預置到 arr2
const arr1 = ['a', 'b'];
.unshift('x', 'y'); // prepend single elements
arr1.deepEqual(arr1, ['x', 'y', 'a', 'b']);
assert
const arr2 = ['a', 'b'];
.unshift(...['x', 'y']); // prepend Array
arr2.deepEqual(arr2, ['x', 'y', 'a', 'b']); assert
散佈讓我們可以將陣列 unshift 到 arr2
。
非破壞性預置是透過散佈元素完成的
const arr1 = ['a', 'b'];
.deepEqual(
assert'x', 'y', ...arr1], // prepend single elements
['x', 'y', 'a', 'b']);
[.deepEqual(arr1, ['a', 'b']); // unchanged!
assert
const arr2 = ['a', 'b'];
.deepEqual(
assert...['x', 'y'], ...arr2], // prepend Array
['x', 'y', 'a', 'b']);
[.deepEqual(arr2, ['a', 'b']); // unchanged! assert
在以下程式碼中,我們以破壞性方式將單一元素附加到 arr1
,並將陣列附加到 arr2
const arr1 = ['a', 'b'];
.push('x', 'y'); // append single elements
arr1.deepEqual(arr1, ['a', 'b', 'x', 'y']);
assert
const arr2 = ['a', 'b'];
.push(...['x', 'y']); // (A) append Array
arr2.deepEqual(arr2, ['a', 'b', 'x', 'y']); assert
散佈 (...
) 讓我們可以將陣列 push 到 arr2
(A 行)。
非破壞性附加是透過散佈元素完成的
const arr1 = ['a', 'b'];
.deepEqual(
assert...arr1, 'x', 'y'], // append single elements
['a', 'b', 'x', 'y']);
[.deepEqual(arr1, ['a', 'b']); // unchanged!
assert
const arr2 = ['a', 'b'];
.deepEqual(
assert...arr2, ...['x', 'y']], // append Array
['a', 'b', 'x', 'y']);
[.deepEqual(arr2, ['a', 'b']); // unchanged! assert
以下是移除陣列元素的三種破壞性方式
// Destructively remove first element:
const arr1 = ['a', 'b', 'c'];
.equal(arr1.shift(), 'a');
assert.deepEqual(arr1, ['b', 'c']);
assert
// Destructively remove last element:
const arr2 = ['a', 'b', 'c'];
.equal(arr2.pop(), 'c');
assert.deepEqual(arr2, ['a', 'b']);
assert
// Remove one or more elements anywhere:
const arr3 = ['a', 'b', 'c', 'd'];
.deepEqual(arr3.splice(1, 2), ['b', 'c']);
assert.deepEqual(arr3, ['a', 'd']); assert
.splice()
在 本章末尾的快速參考 中有更詳細的說明。
透過 rest 元素進行解構讓我們可以從陣列開頭非破壞性地移除元素(解構會在 稍後 說明)。
const arr1 = ['a', 'b', 'c'];
// Ignore first element, extract remaining elements
const [, ...arr2] = arr1;
.deepEqual(arr2, ['b', 'c']);
assert.deepEqual(arr1, ['a', 'b', 'c']); // unchanged! assert
唉,rest 元素必須出現在陣列的最後。因此,我們只能使用它來萃取字尾。
練習:透過陣列實作佇列
exercises/arrays/queue_via_array_test.mjs
.find()
、.map()
、.filter()
等)在本節中,我們將探討用於迭代陣列和轉換陣列的陣列方法。
所有迭代和轉換方法都使用回呼函式。前者將所有迭代值傳送給它們的回呼函式;後者詢問它們的回呼函式如何轉換陣列。
這些回呼函式的類型簽章如下所示
: (value: T, index: number, array: Array<T>) => boolean callback
也就是說,回呼函式會取得三個參數(它可以忽略其中任何一個)
value
是最重要的參數。此參數包含目前正在處理的迭代值。index
另外可以告訴回呼函式迭代值的索引為何。array
指向目前的陣列(方法呼叫的接收者)。有些演算法需要參照整個陣列,例如搜尋陣列以找出答案。此參數讓我們可以為此類演算法撰寫可重複使用的回呼函式。回呼函式預期回傳的內容取決於它傳遞給的方法。可能性包括
.map()
會以其回呼函式回傳的值填滿其結果
> ['a', 'b', 'c'].map(x => x + x)[ 'aa', 'bb', 'cc' ]
.find()
會傳回第一個陣列元素,其 callback 會傳回 true
> ['a', 'bb', 'ccc'].find(str => str.length >= 2)'bb'
稍後會更詳細說明這兩個方法。
.find()
、.findIndex()
.find()
會傳回第一個元素,其 callback 會傳回真值 (如果找不到任何項目,則傳回 undefined
)
> [6, -5, 8].find(x => x < 0)-5
> [6, 5, 8].find(x => x < 0)undefined
.findIndex()
會傳回第一個元素的索引,其 callback 會傳回真值 (如果找不到任何項目,則傳回 -1
)
> [6, -5, 8].findIndex(x => x < 0)1
> [6, 5, 8].findIndex(x => x < 0)-1
.findIndex()
可以實作如下
function findIndex(arr, callback) {
for (const [i, x] of arr.entries()) {
if (callback(x, i, arr)) {
return i;
}
}return -1;
}
.map()
:複製並提供元素新值.map()
會傳回接收者的修改後副本。副本的元素是將 map
的 callback 套用至接收者元素的結果。
透過範例可以更輕鬆地理解所有這些內容
> [1, 2, 3].map(x => x * 3)[ 3, 6, 9 ]
> ['how', 'are', 'you'].map(str => str.toUpperCase())[ 'HOW', 'ARE', 'YOU' ]
> [true, true, true].map((_x, index) => index)[ 0, 1, 2 ]
.map()
可以實作如下
function map(arr, mapFunc) {
const result = [];
for (const [i, x] of arr.entries()) {
.push(mapFunc(x, i, arr));
result
}return result;
}
練習:透過
.map()
編號行
exercises/arrays/number_lines_test.mjs
.flatMap()
:對應到零個或多個值Array<T>.prototype.flatMap()
的類型簽章為
.flatMap<U>(
: (value: T, index: number, array: T[]) => U|Array<U>,
callback?: any
thisValue: U[] )
.map()
和 .flatMap()
都將函式 callback
作為參數,用來控制輸入陣列如何轉換為輸出陣列
.map()
時,每個輸入陣列元素都會轉換為一個輸出元素。也就是說,callback
會傳回一個單一值。.flatMap()
時,每個輸入陣列元素都會轉換為零個或多個輸出元素。也就是說,callback
會傳回一個值陣列 (它也可以傳回非陣列值,但這很罕見)。這是 .flatMap()
的實際應用
> ['a', 'b', 'c'].flatMap(x => [x,x])[ 'a', 'a', 'b', 'b', 'c', 'c' ]
> ['a', 'b', 'c'].flatMap(x => [x])[ 'a', 'b', 'c' ]
> ['a', 'b', 'c'].flatMap(x => [])[]
我們會在探討如何實作這個方法之前考慮用例。
陣列方法 .map()
的結果總與它被呼叫的陣列長度相同。也就是說,其 callback 無法略過它不感興趣的陣列元素。.flatMap()
能夠這樣做,這在以下範例中很有用。
我們會使用下列函式 processArray()
來建立一個陣列,然後我們會透過 .flatMap()
過濾並對應它
function processArray(arr, callback) {
return arr.map(x => {
try {
return { value: callback(x) };
catch (e) {
} return { error: e };
};
}) }
接下來,我們透過 processArray()
建立一個陣列 results
const results = processArray([1, -5, 6], throwIfNegative);
.deepEqual(results, [
assertvalue: 1 },
{ error: new Error('Illegal value: -5') },
{ value: 6 },
{ ;
])
function throwIfNegative(value) {
if (value < 0) {
throw new Error('Illegal value: '+value);
}return value;
}
現在我們可以使用 .flatMap()
從 results
中僅擷取值或僅擷取錯誤
const values = results.flatMap(
=> result.value ? [result.value] : []);
result .deepEqual(values, [1, 6]);
assert
const errors = results.flatMap(
=> result.error ? [result.error] : []);
result .deepEqual(errors, [new Error('Illegal value: -5')]); assert
陣列方法 .map()
會將每個輸入陣列元素對應到一個輸出元素。但是,如果我們想要將它對應到多個輸出元素呢?
這在以下範例中是必要的
> stringsToCodePoints(['many', 'a', 'moon'])['m', 'a', 'n', 'y', 'a', 'm', 'o', 'o', 'n']
我們想將字串陣列轉換為 Unicode 字元陣列(代碼點)。下列函式透過 .flatMap()
達到此目的
function stringsToCodePoints(strs) {
return strs.flatMap(str => Array.from(str));
}
我們可以實作 .flatMap()
如下。注意:此實作比內建版本更簡單,例如,它執行更多檢查。
function flatMap(arr, mapFunc) {
const result = [];
for (const [index, elem] of arr.entries()) {
const x = mapFunc(elem, index, arr);
// We allow mapFunc() to return non-Arrays
if (Array.isArray(x)) {
.push(...x);
resultelse {
} .push(x);
result
}
}return result;
}
練習:
.flatMap()
exercises/arrays/convert_to_numbers_test.mjs
exercises/arrays/replace_objects_test.mjs
.filter()
:僅保留部分元素陣列方法 .filter()
會傳回一個陣列,收集所有呼叫回傳為真值的元素。
例如
> [-1, 2, 5, -7, 6].filter(x => x >= 0)[ 2, 5, 6 ]
> ['a', 'b', 'c', 'd'].filter((_x,i) => (i%2)===0)[ 'a', 'c' ]
.filter()
可以實作如下
function filter(arr, filterFunc) {
const result = [];
for (const [i, x] of arr.entries()) {
if (filterFunc(x, i, arr)) {
.push(x);
result
}
}return result;
}
練習:透過
.filter()
移除空白行
exercises/arrays/remove_empty_lines_filter_test.mjs
.reduce()
:從陣列推導值(進階)方法 .reduce()
是用於計算陣列 arr
「摘要」的強大工具。摘要可以是任何類型的值
arr
中所有元素的總和。arr
的副本,其中每個元素都是原始元素的兩倍。reduce
在函數式程式設計中也稱為 foldl
(「向左折疊」),並在其中很受歡迎。一個需要注意的是,它可能會讓程式碼難以理解。
.reduce()
在 Array<T>
內具有下列類型簽章
.reduce<U>(
: (accumulator: U, element: T, index: number, array: T[]) => U,
callback?: U)
init: U
T
是陣列元素的類型,U
是摘要的類型。兩者可能相同或不同。accumulator
只是「摘要」的另一個名稱。
為了計算陣列 arr
的摘要,.reduce()
會一次將所有陣列元素提供給其呼叫回傳
const accumulator_0 = callback(init, arr[0]);
const accumulator_1 = callback(accumulator_0, arr[1]);
const accumulator_2 = callback(accumulator_1, arr[2]);
// Etc.
callback
會將先前計算的摘要(儲存在其參數 accumulator
中)與目前的陣列元素結合,並傳回下一個 accumulator
。.reduce()
的結果是最後的累加器,也就是 callback
在拜訪所有元素後最後的結果。
換句話說:callback
會執行大部分的工作;.reduce()
僅以有用的方式呼叫它。
我們可以說呼叫回傳會將陣列元素折疊到累加器中。這就是為什麼在函數式程式設計中,這個操作稱為「折疊」。
讓我們來看一個 .reduce()
實際運作的範例:函式 addAll()
會計算陣列 arr
中所有數字的總和。
function addAll(arr) {
const startSum = 0;
const callback = (sum, element) => sum + element;
return arr.reduce(callback, startSum);
}.equal(addAll([1, 2, 3]), 6); // (A)
assert.equal(addAll([7, -4, 2]), 5); assert
在這種情況下,累加器會儲存所有陣列元素的總和,而 callback
已經拜訪過這些元素。
結果 6
是如何從 A 行的陣列中衍生的?透過以下 callback
呼叫
callback(0, 1) --> 1
callback(1, 2) --> 3
callback(3, 3) --> 6
備註
.reduce()
的 init
參數開始)。callback
的最後一個結果也是 .reduce()
的結果。或者,我們也可以透過 for-of
迴圈來實作 addAll()
function addAll(arr) {
let sum = 0;
for (const element of arr) {
= sum + element;
sum
}return sum;
}
很難說這兩種實作哪一個「比較好」:基於 .reduce()
的實作稍微簡潔一點,而基於 for-of
的實作可能比較容易理解,特別是對於不熟悉函數式程式設計的人來說。
.reduce()
尋找索引下列函式是陣列方法 .indexOf()
的實作。它會傳回給定的 searchValue
在陣列 arr
中出現的第一個索引
const NOT_FOUND = -1;
function indexOf(arr, searchValue) {
return arr.reduce(
, elem, index) => {
(resultif (result !== NOT_FOUND) {
// We have already found something: don’t change anything
return result;
else if (elem === searchValue) {
} return index;
else {
} return NOT_FOUND;
},
};
NOT_FOUND)
}.equal(indexOf(['a', 'b', 'c'], 'b'), 1);
assert.equal(indexOf(['a', 'b', 'c'], 'x'), -1); assert
.reduce()
的一個限制是我們無法提早結束(在 for-of
迴圈中,我們可以使用 break
)。在這裡,一旦我們找到結果,就會立即傳回結果。
函式 double(arr)
會傳回 inArr
的一份副本,其中所有元素都乘以 2
function double(inArr) {
return inArr.reduce(
, element) => {
(outArr.push(element * 2);
outArrreturn outArr;
,
};
[])
}.deepEqual(
assertdouble([1, 2, 3]),
2, 4, 6]); [
我們透過將元素推入 []
來修改初始值。一個非破壞性的、更具函數性的 double()
版本如下所示
function double(inArr) {
return inArr.reduce(
// Don’t change `outArr`, return a fresh Array
, element) => [...outArr, element * 2],
(outArr;
[])
}.deepEqual(
assertdouble([1, 2, 3]),
2, 4, 6]); [
這個版本比較優雅,但執行速度較慢,而且會使用更多記憶體。
練習:
.reduce()
.reduce()
進行 map()
:exercises/arrays/map_via_reduce_test.mjs
.reduce()
進行 filter()
:exercises/arrays/filter_via_reduce_test.mjs
.reduce()
進行 countMatches()
:exercises/arrays/count_matches_via_reduce_test.mjs
.sort()
:排序陣列.sort()
具有下列類型定義
sort(compareFunc?: (a: T, b: T) => number): this
預設情況下,.sort()
會對元素的字串表示進行排序。這些表示會透過 <
進行比較。這個運算子會以 字彙順序 進行比較(第一個字元最重要)。我們可以從對數字進行排序時看到這一點
> [200, 3, 10].sort()[ 10, 200, 3 ]
在對人類語言的字串進行排序時,我們需要知道它們會根據其代碼單元值(字元代碼)進行比較
> ['pie', 'cookie', 'éclair', 'Pie', 'Cookie', 'Éclair'].sort()[ 'Cookie', 'Pie', 'cookie', 'pie', 'Éclair', 'éclair' ]
所有未加重音的英文字母都出現在所有未加重音的小寫字母之前,而所有未加重音的小寫字母又出現在所有加重音字母之前。如果我們想要針對人類語言進行適當的排序,可以使用 JavaScript 國際化 API Intl
。
.sort()
以「就地」方式排序;它會變更並傳回接收者
> const arr = ['a', 'c', 'b'];
> arr.sort() === arrtrue
> arr[ 'a', 'b', 'c' ]
我們可以透過參數 compareFunc
自訂排序順序,它必須傳回一個數字,該數字為
a < b
a === b
a > b
記住這些規則的提示
負數「小於」零(等等)。
我們可以使用這個輔助函式來排序數字
function compareNumbers(a, b) {
if (a < b) {
return -1;
else if (a === b) {
} return 0;
else {
} return 1;
}
}.deepEqual(
assert200, 3, 10].sort(compareNumbers),
[3, 10, 200]); [
以下是一個快速且簡陋的替代方案。
> [200, 3, 10].sort((a,b) => a - b)[ 3, 10, 200 ]
這種方法的缺點是
a-b
變成一個很大的正數或負數,則有數字溢位或下溢的風險。如果我們要排序物件,也需要使用比較函式。以下程式碼示範如何依年齡排序物件。
const arr = [ {age: 200}, {age: 3}, {age: 10} ];
.deepEqual(
assert.sort((obj1, obj2) => obj1.age - obj2.age),
arrage: 3 }, { age: 10 }, { age: 200 }] ); [{
練習:依名稱排序物件
exercises/arrays/sort_objects_test.mjs
Array
圖例
R
:方法不會變更陣列(非破壞性)。W
:方法會變更陣列(破壞性)。new Array()
new Array(n)
建立一個長度為 n
的陣列,其中包含 n
個洞
// Trailing commas are always ignored.
// Therefore: number of commas = number of holes
.deepEqual(new Array(3), [,,,]); assert
new Array()
建立一個空的陣列。但是,我建議總是改用 []
。
Array
的靜態方法Array.from<T>(iterable: Iterable<T> | ArrayLike<T>): T[]
[ES6]
Array.from<T,U>(iterable: Iterable<T> | ArrayLike<T>, mapFunc: (v: T, k: number) => U, thisArg?: any): U[]
[ES6]
將一個 iterable 或 類似陣列的物件 轉換成一個陣列。另外,輸入值可以在加入輸出陣列之前透過 mapFunc
轉換。
範例
> Array.from(new Set(['a', 'b'])) // iterable[ 'a', 'b' ]
> Array.from({length: 2, 0:'a', 1:'b'}) // Array-like object[ 'a', 'b' ]
Array.of<T>(...items: T[]): T[]
[ES6]
這個靜態方法主要用於 Array
的子類別,在子類別中它用作自訂陣列文字
class MyArray extends Array {}
.equal(
assert.of('a', 'b') instanceof MyArray, true); MyArray
Array.prototype
的方法.at(index: number): T | undefined
[R, ES2022]
傳回 index
處的陣列元素。如果 index
為負數,則會在使用之前將其加到 .length
(-1 變成 this.length-1
,等等)。
> ['a', 'b', 'c'].at(0)'a'
> ['a', 'b', 'c'].at(-1)'c'
.concat(...items: Array<T[] | T>): T[]
[R, ES3]
傳回一個新的陣列,它是接收者和所有 items
的串接。非陣列參數(例如以下範例中的 'b'
)會視為具有單一元素的陣列。
> ['a'].concat('b', ['c', 'd'])[ 'a', 'b', 'c', 'd' ]
.copyWithin(target: number, start: number, end=this.length): this
[W, ES6]
將索引範圍從(包含)start
到(不包含)end
的元素複製到從 target
開始的索引。正確處理重疊。
> ['a', 'b', 'c', 'd'].copyWithin(0, 2, 4)[ 'c', 'd', 'c', 'd' ]
如果 start
或 end
為負數,則會加上 .length
。
.entries(): Iterable<[number, T]>
[R, ES6]
傳回 [索引, 元素] 成對的 Iterable。
> Array.from(['a', 'b'].entries())[ [ 0, 'a' ], [ 1, 'b' ] ]
.every(callback: (value: T, index: number, array: Array<T>) => boolean, thisArg?: any): boolean
[R, ES5]
如果 callback
對每個元素傳回真值,則傳回 true
。否則,傳回 false
。一旦收到假值,就會停止。此方法對應於數學中的普遍量化(「對所有」,∀
)。
> [1, 2, 3].every(x => x > 0)true
> [1, -2, 3].every(x => x > 0)false
相關方法:.some()
(「存在」)。
.fill(value: T, start=0, end=this.length): this
[W, ES6]
將 value
指定給 (包含) start
和 (不包含) end
之間的每個索引。
> [0, 1, 2].fill('a')[ 'a', 'a', 'a' ]
注意事項:不要使用此方法來使用物件 obj
填滿陣列;每個元素將會參考 obj
(共用它)。在此情況下,最好 使用 Array.from()
。
.filter(callback: (value: T, index: number, array: Array<T>) => any, thisArg?: any): T[]
[R, ES5]
傳回一個陣列,其中只包含 callback
傳回真值的元素。
> [1, -2, 3].filter(x => x > 0)[ 1, 3 ]
.find(predicate: (value: T, index: number, obj: T[]) => boolean, thisArg?: any): T | undefined
[R, ES6]
結果是 predicate
傳回真值的第一個元素。如果沒有這樣的元素,則結果為 undefined
。
> [1, -2, 3].find(x => x < 0)-2
> [1, 2, 3].find(x => x < 0)undefined
.findIndex(predicate: (value: T, index: number, obj: T[]) => boolean, thisArg?: any): number
[R, ES6]
結果是 predicate
傳回真值的第一個元素的索引。如果沒有這樣的元素,則結果為 -1
。
> [1, -2, 3].findIndex(x => x < 0)1
> [1, 2, 3].findIndex(x => x < 0)-1
.flat(depth = 1): any[]
[R, ES2019]
「扁平化」陣列:它會深入輸入陣列中嵌套的陣列,並建立一個副本,其中在層級 depth
或更低層級找到的所有值都會移到最上層。
> [ 1,2, [3,4], [[5,6]] ].flat(0) // no change[ 1, 2, [3,4], [[5,6]] ]
> [ 1,2, [3,4], [[5,6]] ].flat(1)[1, 2, 3, 4, [5,6]]
> [ 1,2, [3,4], [[5,6]] ].flat(2)[1, 2, 3, 4, 5, 6]
.flatMap<U>(callback: (value: T, index: number, array: T[]) => U|Array<U>, thisValue?: any): U[]
[R, ES2019]
結果是針對原始陣列的每個元素呼叫 callback()
,並串接它傳回的陣列。
> ['a', 'b', 'c'].flatMap(x => [x,x])[ 'a', 'a', 'b', 'b', 'c', 'c' ]
> ['a', 'b', 'c'].flatMap(x => [x])[ 'a', 'b', 'c' ]
> ['a', 'b', 'c'].flatMap(x => [])[]
.forEach(callback: (value: T, index: number, array: Array<T>) => void, thisArg?: any): void
[R, ES5]
針對每個元素呼叫 callback
。
'a', 'b'].forEach((x, i) => console.log(x, i))
[
// Output:
// 'a', 0
// 'b', 1
for-of
迴圈通常是更好的選擇:它比較快,支援 break
,而且可以迭代任意可迭代物件。
.includes(searchElement: T, fromIndex=0): boolean
[R, ES2016]
如果接收者有一個元素的值為 searchElement
,則傳回 true
,否則傳回 false
。搜尋從索引 fromIndex
開始。
> [0, 1, 2].includes(1)true
> [0, 1, 2].includes(5)false
.indexOf(searchElement: T, fromIndex=0): number
[R, ES5]
傳回第一個與 searchElement
嚴格相等的元素的索引。如果沒有這樣的元素,則傳回 -1
。從索引 fromIndex
開始搜尋,接著拜訪較高的索引。
> ['a', 'b', 'a'].indexOf('a')0
> ['a', 'b', 'a'].indexOf('a', 1)2
> ['a', 'b', 'a'].indexOf('c')-1
.join(separator = ','): string
[R, ES1]
透過串接所有元素的字串表示,並使用 separator
將它們分隔開來,建立一個字串。
> ['a', 'b', 'c'].join('##')'a##b##c'
> ['a', 'b', 'c'].join()'a,b,c'
.keys(): Iterable<number>
[R, ES6]
傳回接收者鍵值的 iterable。
> Array.from(['a', 'b'].keys())[ 0, 1 ]
.lastIndexOf(searchElement: T, fromIndex=this.length-1): number
[R, ES5]
傳回最後一個與 searchElement
嚴格相等的元素的索引。如果沒有這樣的元素,則傳回 -1
。從索引 fromIndex
開始搜尋,接著拜訪較低的索引。
> ['a', 'b', 'a'].lastIndexOf('a')2
> ['a', 'b', 'a'].lastIndexOf('a', 1)0
> ['a', 'b', 'a'].lastIndexOf('c')-1
.map<U>(mapFunc: (value: T, index: number, array: Array<T>) => U, thisArg?: any): U[]
[R, ES5]
傳回一個新的陣列,其中每個元素都是 mapFunc
套用在接收者對應元素的結果。
> [1, 2, 3].map(x => x * 2)[ 2, 4, 6 ]
> ['a', 'b', 'c'].map((x, i) => i)[ 0, 1, 2 ]
.pop(): T | undefined
[W, ES3]
移除並傳回接收者的最後一個元素。也就是說,它將接收者的結尾視為堆疊。與 .push()
相反。
> const arr = ['a', 'b', 'c'];
> arr.pop()'c'
> arr[ 'a', 'b' ]
.push(...items: T[]): number
[W, ES3]
在接收者的結尾新增零個或多個 items
。也就是說,它將接收者的結尾視為堆疊。變更後傳回的值是接收者的長度。與 .pop()
相反。
> const arr = ['a', 'b'];
> arr.push('c', 'd')4
> arr[ 'a', 'b', 'c', 'd' ]
我們可以透過將陣列散布 (...
) 成參數,來推入陣列
> const arr = ['x'];
> arr.push(...['y', 'z'])3
> arr[ 'x', 'y', 'z' ]
.reduce<U>(callback: (accumulator: U, element: T, index: number, array: T[]) => U, init?: U): U
[R, ES5]
此方法產生接收者的摘要:它將所有陣列元素提供給 callback
,而 callback
會將目前的摘要 (在參數 accumulator
中) 與目前的陣列元素結合,並傳回下一個 accumulator
const accumulator_0 = callback(init, arr[0]);
const accumulator_1 = callback(accumulator_0, arr[1]);
const accumulator_2 = callback(accumulator_1, arr[2]);
// Etc.
.reduce()
的結果是 callback
拜訪所有陣列元素後的最後結果。
> [1, 2, 3].reduce((accu, x) => accu + x, 0)6
> [1, 2, 3].reduce((accu, x) => accu + String(x), '')'123'
如果未提供 init
,則使用索引 0 處的陣列元素,並首先拜訪索引 1 處的元素。因此,陣列的長度必須至少為 1。
.reduceRight<U>(callback: (accumulator: U, element: T, index: number, array: T[]) => U, init?: U): U
[R, ES5]
作用類似於 .reduce()
,但從最後一個元素開始,反向拜訪陣列元素。
> [1, 2, 3].reduceRight((accu, x) => accu + String(x), '')'321'
.reverse(): this
[W, ES1]
重新排列接收者的元素,使其順序相反,然後傳回接收者。
> const arr = ['a', 'b', 'c'];
> arr.reverse()[ 'c', 'b', 'a' ]
> arr[ 'c', 'b', 'a' ]
.shift(): T | undefined
[W, ES3]
移除並傳回接收者的第一個元素。與 .unshift()
相反。
> const arr = ['a', 'b', 'c'];
> arr.shift()'a'
> arr[ 'b', 'c' ]
.slice(start=0, end=this.length): T[]
[R, ES3]
傳回一個新的陣列,其中包含接收者的元素,其索引介於(包括)start
和(不包括)end
之間。
> ['a', 'b', 'c', 'd'].slice(1, 3)[ 'b', 'c' ]
> ['a', 'b'].slice() // shallow copy[ 'a', 'b' ]
允許負索引,並將其加到 .length
> ['a', 'b', 'c'].slice(-2)[ 'b', 'c' ]
.some(callback: (value: T, index: number, array: Array<T>) => boolean, thisArg?: any): boolean
[R, ES5]
如果 callback
至少針對一個元素傳回真值,則傳回 true
。否則,傳回 false
。一旦收到真值,就會停止。此方法對應於數學中的存在量化(「存在」,∃
)。
> [1, 2, 3].some(x => x < 0)false
> [1, -2, 3].some(x => x < 0)true
相關方法:.every()
(「對於所有」)。
.sort(compareFunc?: (a: T, b: T) => number): this
[W, ES1]
對接收者進行排序並傳回。預設會對元素的字串表示進行排序。它會根據字元碼單元值(字元代碼)以字彙順序進行排序
> ['pie', 'cookie', 'éclair', 'Pie', 'Cookie', 'Éclair'].sort()[ 'Cookie', 'Pie', 'cookie', 'pie', 'Éclair', 'éclair' ]
> [200, 3, 10].sort()[ 10, 200, 3 ]
我們可以透過 compareFunc
自訂排序順序,它會傳回一個數字,該數字為
a < b
a === b
a > b
對數字進行排序的技巧(有數字溢位或下溢的風險)
> [200, 3, 10].sort((a, b) => a - b)[ 3, 10, 200 ]
.sort()
是穩定的
自 ECMAScript 2019 以來,排序保證是穩定的:如果元素在排序時被視為相等,則排序不會改變這些元素的順序(相對於彼此)。
.splice(start: number, deleteCount=this.length-start, ...items: T[]): T[]
[W, ES3]
在索引 start
處,它移除 deleteCount
個元素並插入 items
。它會傳回已刪除的元素。
> const arr = ['a', 'b', 'c', 'd'];
> arr.splice(1, 2, 'x', 'y')[ 'b', 'c' ]
> arr[ 'a', 'x', 'y', 'd' ]
start
可以為負值,如果為負值,則會將其加到 .length
> ['a', 'b', 'c'].splice(-2, 2)[ 'b', 'c' ]
.toString(): string
[R, ES1]
透過 String()
將所有元素轉換為字串,將它們串接起來,並以逗號分隔,然後傳回結果。
> [1, 2, 3].toString()'1,2,3'
> ['1', '2', '3'].toString()'1,2,3'
> [].toString()''
.unshift(...items: T[]): number
[W, ES3]
在接收者的開頭插入 items
,並在修改後傳回其長度。
> const arr = ['c', 'd'];
> arr.unshift('e', 'f')4
> arr[ 'e', 'f', 'c', 'd' ]
.values(): Iterable<T>
[R, ES6]
傳回接收者值的 iterable。
> Array.from(['a', 'b'].values())[ 'a', 'b' ]
測驗
請參閱 測驗應用程式。