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()の基本的な機能は、2種類の値を配列に変換することです。
lengthプロパティとインデックス付き要素を持っています。例として、document.getElementsByClassName()などのDOM操作の結果があります。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()の2パラメータバージョンを使用して、その中間ステップをスキップしました。
from() Array.from()のもう1つのユースケースは、配列のような値または反復可能な値を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のサブコンストラクターは、of()を含むArrayのすべてのメソッドを継承することを覚えておいてください)。
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()前述の各メソッドの結果は値のシーケンスですが、配列として返されるのではなく、イテレーターを介して1つずつ明らかにされます。例を見てみましょう。イテレーターの内容を配列に入れるために、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とその後のインデックスにコピーします。2つのインデックス範囲が重なる場合、すべてのソース要素が上書きされる前にコピーされるように注意が払われます。
例
> 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
このセクションでは、穴に関する多くの例が表示されます。不明な点がある場合は、「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()を2回呼び出すと、インデックス0に穴があり、インデックス1に要素'a'があります。ご覧のとおり、前者はundefinedを生成します。
> iter.next()
{ value: undefined, done: false }
> iter.next()
{ value: 'a', done: false }
とりわけ、2つの操作が反復プロトコルに基づいています。したがって、これらの操作も穴を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' ]
2番目の引数を指定すると、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'] |
copyWithinES6 |
保持される | [,'a','b',,].copyWithin(2,0) → [,'a',,'a'] |
entriesES6 |
要素 | [...[,'a'].entries()] → [[0,undefined], [1,'a']] |
every |
無視される | [,'a'].every(x => x==='a') → true |
fillES6 |
埋められる | new Array(3).fill('a') → ['a','a','a'] |
filter |
削除される | ['a',,'b'].filter(x => true) → ['a','b'] |
findES6 |
要素 | [,'a'].find(x => true) → undefined |
findIndexES6 |
要素 | [,'a'].findIndex(x => true) → 0 |
forEach |
無視される | [,'a'].forEach((x,i) => log(i)); → 1 |
indexOf |
無視される | [,'a'].indexOf(undefined) → -1 |
join |
要素 | [,'a',undefined,null].join('#') → '#a##' |
keysES6 |
要素 | [...[,'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 |
valuesES6 |
要素 | [...[,'a'].values()] → [undefined,'a'] |
注
['a',,].length → 2const log = console.log.bind(console);新しいES6操作で穴がundefined要素として扱われることは、値で埋められた配列の作成に役立ちます。
Array.prototype.fill()は、すべての配列要素(穴を含む)を固定値で置き換えます。
> new Array(3).fill(7)
[ 7, 7, 7 ]
new Array(3)は3つの穴を持つ配列を作成し、fill()は各穴を値7で置き換えます。
Array.prototype.keys()は、配列に穴しかない場合でもキーを報告します。これはイテラブルを返し、スプレッド演算子を使って配列に変換できます。
> [...new Array(3).keys()]
[ 0, 1, 2 ]
Array.from()の2番目のパラメータのマップ関数は、穴を通知されます。したがって、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 Arraysは、より大きなインデックス範囲を持っています。0 ≤ i < 253−1。その範囲の上限は、253−1がJavaScriptの浮動小数点数が安全に表現できる最大の整数であるためです。詳細については、「安全な整数」を参照してください。
通常の配列のインデックス範囲が小さい唯一の理由は、下位互換性です。