Array
方法
Array.from(arrayLike, mapFunc?, thisArg?)
Array.of(...items)
Array.prototype
方法
Array.prototype.copyWithin()
Array.prototype.fill()
undefined
元素concat()
(Symbol.isConcatSpreadable
)擴散
Symbol.isConcatSpreadable
新的靜態Array
方法
Array.from(arrayLike, mapFunc?, thisArg?)
Array.of(...items)
新的Array.prototype
方法
Array.prototype.entries()
Array.prototype.keys()
Array.prototype.values()
Array.prototype.find(predicate, thisArg?)
Array.prototype.findIndex(predicate, thisArg?)
Array.prototype.copyWithin(target, start, end=this.length)
Array.prototype.fill(value, start=0, end=this.length)
Array
方法 Array
物件有新的方法。
Array.from(arrayLike, mapFunc?, thisArg?)
Array.from()
的基本功能是將兩種值轉換成陣列
length
和索引元素。範例包括 DOM 操作的結果,例如 document.getElementsByClassName()
。Map
和 Set
也是可迭代的。以下是將類陣列物件轉換為陣列的範例
const
arrayLike
=
{
length
:
2
,
0
:
'a'
,
1
:
'b'
};
// for-of only works with iterable values
for
(
const
x
of
arrayLike
)
{
// TypeError
console
.
log
(
x
);
}
const
arr
=
Array
.
from
(
arrayLike
);
for
(
const
x
of
arr
)
{
// OK, iterable
console
.
log
(
x
);
}
// Output:
// a
// b
Array.from()
進行對應 Array.from()
也是使用 map()
泛型 的便利替代方案
const
spans
=
document
.
querySelectorAll
(
'span.name'
);
// map(), generically:
const
names1
=
Array
.
prototype
.
map
.
call
(
spans
,
s
=>
s
.
textContent
);
// Array.from():
const
names2
=
Array
.
from
(
spans
,
s
=>
s
.
textContent
);
在此範例中,document.querySelectorAll()
的結果再次是類陣列物件,而不是陣列,這就是我們無法在其上呼叫 map()
的原因。先前,我們將類陣列物件轉換為陣列,以便呼叫 forEach()
。在此,我們透過泛型方法呼叫和 Array.from()
的兩個參數版本略過中間步驟。
Array
子類別中的 from()
Array.from()
的另一個使用案例是將類陣列或可迭代值轉換為 Array
子類別的執行個體。例如,如果您建立 Array
的子類別 MyArray
,並想要將此類物件轉換為 MyArray
的執行個體,您只需使用 MyArray.from()
。之所以可行,是因為建構函式在 ECMAScript 6 中會彼此繼承(超級建構函式是其子建構函式的原型)。
class
MyArray
extends
Array
{
···
}
const
instanceOfMyArray
=
MyArray
.
from
(
anIterable
);
您也可以將此功能與對應結合,以取得您控制結果建構函式的對應操作
// from() – determine the result’s constructor via the receiver
// (in this case, MyArray)
const
instanceOfMyArray
=
MyArray
.
from
([
1
,
2
,
3
],
x
=>
x
*
x
);
// map(): the result is always an instance of Array
const
instanceOfArray
=
[
1
,
2
,
3
].
map
(
x
=>
x
*
x
);
種類模式讓您可以設定非靜態內建方法(例如 slice()
、filter()
和 map()
)傳回的執行個體。它在「類別」章節的「種類模式」節中說明。
Array.of(...items)
Array.of(item_0, item_1, ···)
會建立一個元素為 item_0
、item_1
等的陣列。
Array.of()
作為 Array
子類別的陣列文字 如果您想要將幾個值轉換為陣列,您應該總是使用陣列文字,特別是如果有一個值是數字,陣列建構函式無法正常運作(有關此怪癖的更多資訊)
> new Array(3, 11, 8)
[ 3, 11, 8 ]
> new Array(3)
[ , , ,]
> new Array(3.1)
RangeError: Invalid array length
但是,您應該如何將值轉換為 Array
的子建構函式的執行個體呢?這時 Array.of()
就派上用場了(請記住,Array
的子建構函式會繼承 Array
的所有方法,包括 of()
)。
class
MyArray
extends
Array
{
···
}
console
.
log
(
MyArray
.
of
(
3
,
11
,
8
)
instanceof
MyArray
);
// true
console
.
log
(
MyArray
.
of
(
3
).
length
===
1
);
// true
Array.prototype
方法 陣列實例有幾個新的方法可用。
下列方法有助於迭代陣列
Array.prototype.entries()
Array.prototype.keys()
Array.prototype.values()
上述每個方法的結果都是一個值序列,但它們不會作為陣列傳回;它們會透過一個迭代器逐一顯示。我們來看一個範例。我使用 Array.from()
將迭代器的內容放入陣列中
> Array.from(['a', 'b'].keys())
[ 0, 1 ]
> Array.from(['a', 'b'].values())
[ 'a', 'b' ]
> Array.from(['a', 'b'].entries())
[ [ 0, 'a' ],
[ 1, 'b' ] ]
我也可以使用 展開運算子 (...
) 將迭代器轉換為陣列
> [...['a', 'b'].keys()]
[ 0, 1 ]
[index, element]
成對 您可以將 entries()
與 ECMAScript 6 的 for-of
迴圈和解構結合,以方便地迭代 [index, element]
成對
for
(
const
[
index
,
element
]
of
[
'a'
,
'b'
].
entries
())
{
console
.
log
(
index
,
element
);
}
Array.prototype.find(predicate, thisArg?)
傳回第一個陣列元素,其中呼叫回函 predicate
傳回 true
。如果沒有這樣的元素,它會傳回 undefined
。範例
> [6, -5, 8].find(x => x < 0)
-5
> [6, 5, 8].find(x => x < 0)
undefined
Array.prototype.findIndex(predicate, thisArg?)
傳回第一個元素的索引,其中呼叫回函 predicate
傳回 true
。如果沒有這樣的元素,它會傳回 -1
。範例
> [6, -5, 8].findIndex(x => x < 0)
1
> [6, 5, 8].findIndex(x => x < 0)
-1
呼叫回函 predicate
的完整簽章是
predicate
(
element
,
index
,
array
)
findIndex()
尋找 NaN
Array.prototype.indexOf()
的一個眾所周知的 限制 是它找不到 NaN
,因為它會透過 ===
搜尋元素
> [NaN].indexOf(NaN)
-1
使用 findIndex()
,您可以使用 Object.is()
(在 OOP 章節 中說明),並且不會有這樣的問題
> [NaN].findIndex(y => Object.is(NaN, y))
0
您也可以採用更通用的方法,建立一個輔助函式 elemIs()
> function elemIs(x) { return Object.is.bind(Object, x) }
> [NaN].findIndex(elemIs(NaN))
0
Array.prototype.copyWithin()
此方法的簽章是
Array
.
prototype
.
copyWithin
(
target
:
number
,
start
:
number
,
end
=
this
.
length
)
:
This
它會將索引在範圍 [start
,end
) 內的元素複製到索引 target
及後續索引。如果兩個索引範圍重疊,則會小心處理,以確保所有來源元素在覆寫之前都已複製。
範例
>
const
arr
=
[
0
,
1
,
2
,
3
];
>
arr
.
copyWithin
(
2
,
0
,
2
)
[
0
,
1
,
0
,
1
]
>
arr
[
0
,
1
,
0
,
1
]
Array.prototype.fill()
此方法的簽章是
Array
.
prototype
.
fill
(
value
:
any
,
start
=
0
,
end
=
this
.
length
)
:
This
它會使用提供的 value
填滿陣列
> const arr = ['a', 'b', 'c'];
> arr.fill(7)
[ 7, 7, 7 ]
> arr
[ 7, 7, 7 ]
選擇性地,你可以限制填滿的起始和結束位置
> ['a', 'b', 'c'].fill(7, 1, 2)
[ 'a', 7, 'c' ]
洞是陣列「內部」的索引,沒有關聯的元素。換句話說:如果陣列 arr
在索引 i
處有一個洞,則
i
< arr.length
!(i in arr)
例如:以下陣列在索引 1 處有一個洞。
> const arr = ['a',,'b']
'use strict'
> 0 in arr
true
> 1 in arr
false
> 2 in arr
true
> arr[1]
undefined
你會在本章節中看到許多包含洞的範例。如果任何地方不清楚,你可以參閱「Speaking JavaScript」中的「陣列中的洞」章節,以取得更多資訊。
undefined
元素 ES6 中陣列方法的一般規則是:每個洞都視為元素 undefined
。範例
> Array.from(['a',,'b'])
[ 'a', undefined, 'b' ]
> [,'a'].findIndex(x => x === undefined)
0
> [...[,'a'].entries()]
[ [ 0, undefined ], [ 1, 'a' ] ]
這個想法是引導人們遠離洞,並從長遠來看簡化。不幸的是,這表示事情現在更加不一致。
由 Array.prototype[Symbol.iterator]
建立的迭代器將每個洞視為元素 undefined
。例如,以下迭代器 iter
> var arr = [, 'a'];
> var iter = arr[Symbol.iterator]();
如果我們呼叫 next()
兩次,我們會取得索引 0 處的洞和索引 1 處的元素 'a'
。如你所見,前者會產生 undefined
> iter.next()
{ value: undefined, done: false }
> iter.next()
{ value: 'a', done: false }
在其他方法中,有兩個操作是基於 反覆運算協定。因此,這些操作也會將洞視為 undefined
元素。
首先,展開運算子 (...
)
> [...[, 'a']]
[ undefined, 'a' ]
其次,for-of
迴圈
for
(
const
x
of
[,
'a'
])
{
console
.
log
(
x
);
}
// Output:
// undefined
// a
請注意,陣列原型方法 (filter()
等) 沒有使用反覆運算協定。
Array.from()
如果其參數是可迭代的,Array.from()
使用迭代將其轉換為陣列。然後它會完全像展開運算子一樣運作
> Array.from([, 'a'])
[ undefined, 'a' ]
但是 Array.from()
也可以將 類陣列物件 轉換為陣列。然後洞也會變成 undefined
> Array.from({1: 'a', length: 2})
[ undefined, 'a' ]
使用第二個參數時,Array.from()
的運作方式與 Array.prototype.map()
幾乎相同。
然而,Array.from()
將洞視為 undefined
> Array.from([,'a'], x => x)
[ undefined, 'a' ]
> Array.from([,'a'], (x,i) => i)
[ 0, 1 ]
Array.prototype.map()
會略過它們,但會保留它們
> [,'a'].map(x => x)
[ , 'a' ]
> [,'a'].map((x,i) => i)
[ , 1 ]
Array.prototype
方法 在 ECMAScript 5 中,行為已經略有不同。例如
forEach()
、filter()
、every()
和 some()
會忽略洞。map()
會略過但保留洞。join()
和 toString()
將洞視為 undefined
元素,但會將 null
和 undefined
都解釋為空字串。ECMAScript 6 加入新的行為類型
copyWithin()
在複製洞時會建立洞(即必要時刪除元素)。entries()
、keys()
、values()
將每個洞視為元素 undefined
。find()
和 findIndex()
也是如此。fill()
不管索引處是否有元素。下表說明 Array.prototype
方法如何處理洞。
方法 | 洞 | |
---|---|---|
concat |
保留 | ['a',,'b'].concat(['c',,'d']) → ['a',,'b','c',,'d'] |
copyWithin ES6
|
保留 | [,'a','b',,].copyWithin(2,0) → [,'a',,'a'] |
entries ES6
|
元素 | [...[,'a'].entries()] → [[0,undefined], [1,'a']] |
every |
忽略 | [,'a'].every(x => x==='a') → true |
fill ES6
|
填滿 | new Array(3).fill('a') → ['a','a','a'] |
filter |
移除 | ['a',,'b'].filter(x => true) → ['a','b'] |
find ES6
|
元素 | [,'a'].find(x => true) → undefined |
findIndex ES6
|
元素 | [,'a'].findIndex(x => true) → 0 |
forEach |
忽略 | [,'a'].forEach((x,i) => log(i)); → 1 |
indexOf |
忽略 | [,'a'].indexOf(undefined) → -1 |
join |
元素 | [,'a',undefined,null].join('#') → '#a##' |
keys ES6
|
元素 | [...[,'a'].keys()] → [0,1] |
lastIndexOf |
忽略 | [,'a'].lastIndexOf(undefined) → -1 |
map |
保留 | [,'a'].map(x => 1) → [,1] |
pop |
元素 | ['a',,].pop() → undefined |
push |
保留 | new Array(1).push('a') → 2 |
reduce |
忽略 | ['#',,undefined].reduce((x,y)=>x+y) → '#undefined' |
reduceRight |
忽略 | ['#',,undefined].reduceRight((x,y)=>x+y) → 'undefined#' |
reverse |
保留 | ['a',,'b'].reverse() → ['b',,'a'] |
shift |
元素 | [,'a'].shift() → undefined |
slice |
保留 | [,'a'].slice(0,1) → [,] |
some |
忽略 | [,'a'].some(x => x !== 'a') → false |
sort |
保留 | [,undefined,'a'].sort() → ['a',undefined,,] |
splice |
保留 | ['a',,].splice(1,1) → [,] |
toString |
元素 | [,'a',undefined,null].toString() → ',a,,' |
unshift |
保留 | [,'a'].unshift('b') → 3 |
values ES6
|
元素 | [...[,'a'].values()] → [undefined,'a'] |
注意事項
['a',,].length → 2
const log = console.log.bind(console);
ES6 新運算將空洞視為 undefined
元素,有助於建立填入值的陣列。
Array.prototype.fill()
會將所有陣列元素(包括空洞)替換為固定值
> new Array(3).fill(7)
[ 7, 7, 7 ]
new Array(3)
會建立一個有三個空洞的陣列,而 fill()
會將每個空洞替換為值 7
。
Array.prototype.keys()
會回報金鑰,即使陣列只有空洞。它會回傳一個可迭代物件,你可以透過展開運算子將其轉換為陣列
> [...new Array(3).keys()]
[ 0, 1, 2 ]
Array.from()
第二個參數中的對應函式會收到空洞的通知。因此,你可以使用 Array.from()
進行更複雜的填入
> Array.from(new Array(5), (x,i) => i*2)
[ 0, 2, 4, 6, 8 ]
undefined
如果你需要一個填入 undefined
的陣列,你可以利用迭代(由展開運算子觸發)會將空洞轉換為 undefined
的事實
> [...new Array(3)]
[ undefined, undefined, undefined ]
ES5 方法 filter()
讓你移除空洞
> ['a',,'c'].filter(() => true)
[ 'a', 'c' ]
ES6 迭代(透過展開運算子觸發)讓你將空洞轉換成 undefined
元素
> [...['a',,'c']]
[ 'a', undefined, 'c' ]
concat()
展開(Symbol.isConcatSpreadable
) 你可以透過新增一個(自有或繼承的)屬性,其鍵值為眾所周知的符號 Symbol.isConcatSpreadable
,且其值為布林值,來設定 Array.prototype.concat()
如何處理物件。
預設情況下,Array.prototype.concat()
會將陣列展開到其結果中:陣列的索引元素會成為結果的元素
const
arr1
=
[
'c'
,
'd'
];
[
'a'
,
'b'
].
concat
(
arr1
,
'e'
);
// ['a', 'b', 'c', 'd', 'e']
使用 Symbol.isConcatSpreadable
,你可以覆寫預設值並避免展開陣列
const
arr2
=
[
'c'
,
'd'
];
arr2
[
Symbol
.
isConcatSpreadable
]
=
false
;
[
'a'
,
'b'
].
concat
(
arr2
,
'e'
);
// ['a', 'b', ['c','d'], 'e']
對於非陣列,預設值是不展開
const
arrayLike
=
{
length
:
2
,
0
:
'c'
,
1
:
'd'
};
console
.
log
([
'a'
,
'b'
].
concat
(
arrayLike
,
'e'
));
// ['a', 'b', arrayLike, 'e']
console
.
log
(
Array
.
prototype
.
concat
.
call
(
arrayLike
,
[
'e'
,
'f'
],
'g'
));
// [arrayLike, 'e', 'f', 'g']
你可以使用 Symbol.isConcatSpreadable
來強制展開
arrayLike
[
Symbol
.
isConcatSpreadable
]
=
true
;
console
.
log
([
'a'
,
'b'
].
concat
(
arrayLike
,
'e'
));
// ['a', 'b', 'c', 'd', 'e']
console
.
log
(
Array
.
prototype
.
concat
.
call
(
arrayLike
,
[
'e'
,
'f'
],
'g'
));
// ['c', 'd', 'e', 'f', 'g']
concat()
如何判斷參數是否為陣列?它使用與 Array.isArray()
相同的演算法。Array.prototype
是否在原型鏈中,對該演算法沒有影響。這一點很重要,因為在 ES5 及更早版本中,駭客手法用於子類化 Array
,而這些手法必須繼續運作(請參閱本書中關於 __proto__
的章節)
> const arr = [];
> Array.isArray(arr)
true
> Object.setPrototypeOf(arr, null);
> Array.isArray(arr)
true
Symbol.isConcatSpreadable
ES6 標準函式庫中沒有任何物件具有鍵值為 Symbol.isConcatSpreadable
的屬性。因此,此機制純粹存在於瀏覽器 API 和使用者程式碼中。
後果
Array
的子類別預設會被展開(因為它們的實例是陣列物件)。Array
的子類別可以透過將鍵值為 Symbol.isConcatSpreadable
的屬性設定為 false
來防止其實例被展開。該屬性可以是原型屬性或實例屬性。[Symbol.isConcatSpreadable]
為 true
,則其他類陣列物件會被 concat()
展開。例如,這將允許你為某些類陣列 DOM 集合開啟展開。concat()
方法。對於陣列,ES6 仍然有 與 ES5 相同的規則
l
在範圍 0 ≤ l
≤ 232−1 內。i
在範圍 0 ≤ i
< 232−1 內。字串和 Typed Array 有較大的索引範圍:0 ≤ i
< 253−1。該範圍的上限是因為 253−1 是 JavaScript 浮點數可以安全表示的最大整數。有關詳細資訊,請參閱章節「安全整數」。
一般陣列較小索引範圍的唯一原因是向後相容性。