配列とは、インデックス(ゼロから始まる自然数)から任意の値へのマッピングです。値(マップの範囲)は配列の要素と呼ばれます。配列を作成する最も便利な方法は、配列リテラルを使うことです。そのようなリテラルは、配列要素を列挙します。要素の位置は、暗黙的にそのインデックスを指定します。
この章では、まず、インデックスアクセスやlengthプロパティなどの基本的な配列の仕組みを説明し、その後、配列メソッドについて説明します。
このセクションでは、配列の簡単な概要を説明します。詳細については後で説明します。
最初の例として、配列リテラルを使用して配列arrを作成し(「配列の作成」を参照)、要素にアクセスします(「配列のインデックス」を参照)。
> var arr = [ 'a', 'b', 'c' ]; // array literal > arr[0] // get element 0 'a' > arr[0] = 'x'; // set element 0 > arr [ 'x', 'b', 'c' ]
配列のプロパティであるlengthを使用して(「length」を参照)、要素を削除したり追加したりできます。
> var arr = [ 'a', 'b', 'c' ]; > arr.length 3 > arr.length = 2; // remove an element > arr [ 'a', 'b' ] > arr[arr.length] = 'd'; // append an element > arr [ 'a', 'b', 'd' ]
配列メソッドpush()は、要素を追加する別の方法を提供します。
> var arr = [ 'a', 'b' ];
> arr.push('d')
3
> arr
[ 'a', 'b', 'd' ]ECMAScript標準では、配列はインデックスから値へのマップ(辞書)として規定されています。言い換えれば、配列は連続的でなくてもよく、穴があっても構いません。例:
> var arr = []; > arr[0] = 'a'; 'a' > arr[2] = 'b'; 'b' > arr [ 'a', , 'b' ]
上記の配列には穴があります。インデックス1には要素がありません。「配列の穴」で穴について詳しく説明します。
ほとんどのJavaScriptエンジンは、内部的に穴のない配列を最適化し、連続的に格納することに注意してください。
配列は配列リテラルで作成します。
varmyArray=['a','b','c'];
配列の末尾のカンマは無視されます。
> [ 'a', 'b' ].length 2 > [ 'a', 'b', ].length 2 > [ 'a', 'b', ,].length // hole + trailing comma 3
コンストラクターArrayを使用するには、2つの方法があります。指定された長さの空の配列を作成するか、要素が指定された値である配列を作成できます。このコンストラクターでは、newはオプションです。(newなしで)通常の関数として呼び出すことは、コンストラクターとして呼び出すことと同じです。
指定された長さの空の配列には、穴しかありません!したがって、このバージョンのコンストラクターを使用することはほとんど意味がありません。
> var arr = new Array(2); > arr.length 2 > arr // two holes plus trailing comma (ignored!) [ , ,]
一部のエンジンでは、この方法でArray()を呼び出すと、連続したメモリが事前に割り当てられ、パフォーマンスがわずかに向上する場合があります。ただし、冗長性が増すだけの価値があることを確認してください!
// The same as ['a', 'b', 'c']:vararr1=newArray('a','b','c');
問題は、単一の数値を持つ配列を作成できないことです。なぜなら、それはlengthがその数値である配列を作成すると解釈されるためです。
> new Array(2) // alas, not [ 2 ]
[ , ,]
> new Array(5.7) // alas, not [ 5.7 ]
RangeError: Invalid array length
> new Array('abc') // ok
[ 'abc' ]要素に複数の次元が必要な場合は、配列をネストする必要があります。このようなネストされた配列を作成すると、最も内側の配列は必要に応じて成長できます。ただし、要素に直接アクセスする場合は、少なくとも外側の配列を作成する必要があります。次の例では、三目並べ用の3x3の行列を作成します。行列は(必要に応じて行を拡張するのではなく)データで完全に埋められています。
// Create the Tic-tac-toe boardvarrows=[];for(varrowCount=0;rowCount<3;rowCount++){rows[rowCount]=[];for(varcolCount=0;colCount<3;colCount++){rows[rowCount][colCount]='.';}}// Set an X in the upper right cornerrows[0][2]='X';// [row][column]// Print the boardrows.forEach(function(row){console.log(row.join(' '));});
出力は次のとおりです。
. . X . . . . . .
この例では、一般的なケースを示したかったのです。明らかに、行列が非常に小さく、固定された次元を持つ場合は、配列リテラルで設定できます。
varrows=[['.','.','.'],['.','.','.'],['.','.','.']];
配列のインデックスを扱うときは、次の制限事項に注意する必要があります。
範囲外のインデックスは、通常のプロパティキー(文字列!)として扱われます。それらは配列要素として表示されず、プロパティlengthにも影響を与えません。例:
> var arr = []; > arr[-1] = 'a'; > arr [] > arr['-1'] 'a' > arr[4294967296] = 'b'; > arr [] > arr['4294967296'] 'b'
in演算子は、オブジェクトが特定のキーを持つプロパティを持っているかどうかを検出します。ただし、配列に特定の要素インデックスが存在するかどうかを判断するためにも使用できます。例:
> var arr = [ 'a', , 'b' ]; > 0 in arr true > 1 in arr false > 10 in arr false
プロパティの削除に加えて、delete演算子は配列要素も削除します。要素を削除すると穴が作成されます(lengthプロパティは更新されません)。
> var arr = [ 'a', 'b' ]; > arr.length 2 > delete arr[1] // does not update length true > arr [ 'a', ] > arr.length 2
配列の長さを短くして、末尾の配列要素を削除することもできます(詳細については「length」を参照)。穴を作成せずに要素を削除するには(つまり、後続の要素のインデックスがデクリメントされる)、Array.prototype.splice()を使用します(「要素の追加と削除(破壊的)」を参照)。この例では、インデックス1で2つの要素を削除します。
> var arr = ['a', 'b', 'c', 'd']; > arr.splice(1, 2) // returns what has been removed [ 'b', 'c' ] > arr [ 'a', 'd' ]
これは高度なセクションです。通常、ここで説明する詳細を知る必要はありません。
配列のインデックスは見た目どおりではありません。今まで、配列のインデックスは数値であると仮定していました。そして、それがJavaScriptエンジンが内部的に配列を実装する方法です。ただし、ECMAScript仕様では、インデックスは異なるものとして捉えられています。セクション15.4を言い換えると、
P(文字列)は、ToString(ToUint32(P))がPに等しく、ToUint32(P)が232−1に等しくない場合にのみ、配列インデックスです。これが何を意味するのかは、すぐに説明します。言い換えれば、仕様の世界では、括弧内のすべての値は文字列に変換され、数値であってもプロパティキーとして解釈されます。次のやり取りでこれを示します。
> var arr = ['a', 'b']; > arr['0'] 'a' > arr[0] 'a'
配列インデックスであるためには、プロパティキーP(文字列!)は、次の計算の結果と等しくなければなりません。
Pを数値に変換します。つまり、配列インデックスは、32ビット範囲0 ≤ i < 232−1の文字列化された整数iでなければなりません。上限は、(前に引用したように)仕様で明示的に除外されています。最大長の予約済みです。この定義がどのように機能するかを確認するために、「ビット単位演算子による32ビット整数」の関数ToUint32()を使用しましょう。
まず、数値を含まない文字列は常に0に変換されます。これは、文字列化後、文字列と等しくありません。
> ToUint32('xyz')
0
> ToUint32('?@#!')
0次に、範囲外の文字列化された整数も、完全に異なる整数に変換されます。これは、文字列化後、文字列と等しくありません。
> ToUint32('-1')
4294967295
> Math.pow(2, 32)
4294967296
> ToUint32('4294967296')
0第三に、文字列化された非整数数値は整数に変換されます。これもまた異なります。
> ToUint32('1.371')
1仕様では、配列インデックスに指数がないことも強制されています。
> ToUint32('1e3')
1000また、先頭にゼロがないことも強制されています。
> var arr = ['a', 'b']; > arr['0'] // array index 'a' > arr['00'] // normal property undefined
lengthプロパティの基本的な機能は、配列内の最大のインデックスを追跡することです。
> [ 'a', 'b' ].length 2 > [ 'a', , 'b' ].length 3
したがって、lengthは要素の数をカウントしないため、独自の関数を作成する必要があります。例:
functioncountElements(arr){varelemCount=0;arr.forEach(function(){elemCount++;});returnelemCount;}
要素(非穴)をカウントするために、forEachが穴をスキップするという事実を使用しました。これがインタラクションです。
> countElements([ 'a', 'b' ]) 2 > countElements([ 'a', , 'b' ]) 2
配列の長さを手動で増やしても、配列にほとんど影響はありません。穴が作成されるだけです。
> var arr = [ 'a', 'b' ]; > arr.length = 3; > arr // one hole at the end [ 'a', 'b', ,]
最後の結果には末尾に2つのカンマがあります。これは、末尾のカンマがオプションであり、常に無視されるためです。
先ほど行った操作では、要素は追加されませんでした。
> countElements(arr) 2
ただし、lengthプロパティは、新しい要素を挿入する場所を示すポインターとして機能します。例:
> arr.push('c')
4
> arr
[ 'a', 'b', , 'c' ]したがって、Arrayコンストラクターを使用して配列の初期長さを設定すると、完全に空の配列が作成されます。
> var arr = new Array(2); > arr.length 2 > countElements(arr) 0
配列の長さを短くすると、新しい長さ以上のすべての要素が削除されます。
> var arr = [ 'a', 'b', 'c' ]; > 1 in arr true > arr[1] 'b' > arr.length = 1; > arr [ 'a' ] > 1 in arr false > arr[1] undefined
配列とは、インデックスから値へのマップです。つまり、配列には穴、つまり、配列内に欠落している長さよりも小さいインデックスが存在する可能性があります。それらのインデックスの1つにある要素を読み取ると、undefinedが返されます。
配列の穴を避けることをお勧めします。JavaScriptはそれらを矛盾して処理します(つまり、一部のメソッドはそれらを無視し、その他のメソッドは無視しません)。ありがたいことに、通常、穴がどのように処理されるかを知る必要はありません。それらはめったに役に立たず、パフォーマンスに悪影響を与えます。
> var arr = []; > arr[0] = 'a'; > arr[2] = 'c'; > 1 in arr // hole at index 1 false
配列リテラルで値を省略することでも、穴を作成できます。
> var arr = ['a',,'c']; > 1 in arr // hole at index 1 false
末尾の穴を作成するには、末尾のカンマを2つ必要とする必要があります。なぜなら、最後のカンマは常に無視されるためです。
> [ 'a', ].length 1 > [ 'a', ,].length 2
このセクションでは、要素としての穴とundefinedの違いについて検討します。穴を読み取るとundefinedが返されるため、両者は非常によく似ています。
穴のある配列はスパースと呼ばれます。穴のない配列は密と呼ばれます。密な配列は連続しており、ゼロから始まり、length − 1で終わる各インデックスに要素があります。スパース配列と密な配列という、次の2つの配列を比較してみましょう。これら2つは非常によく似ています。
varsparse=[,,'c'];vardense=[undefined,undefined,'c'];
穴は、ほとんど同じインデックスに要素undefinedがあるようなものです。どちらの配列も同じ長さを持っています。
> sparse.length 3 > dense.length 3
しかし、スパース配列はインデックス0に要素を持っていません。
> 0 in sparse false > 0 in dense true
forによる反復は、両方の配列で同じです。
> for (var i=0; i<sparse.length; i++) console.log(sparse[i]); undefined undefined c > for (var i=0; i<dense.length; i++) console.log(dense[i]); undefined undefined c
forEachによる反復は、穴をスキップしますが、undefined要素はスキップしません。
> sparse.forEach(function (x) { console.log(x) });
c
> dense.forEach(function (x) { console.log(x) });
undefined
undefined
c配列に関する操作の中には、穴を無視するものと、穴を考慮するものがあります。このセクションでは、詳細を説明します。
forEach()は穴をスキップします。
> ['a',, 'b'].forEach(function (x,i) { console.log(i+'.'+x) })
0.a
2.bevery()も穴をスキップします(同様に、some()も)。
> ['a',, 'b'].every(function (x) { return typeof x === 'string' })
truemap()はスキップしますが、穴を保持します。
> ['a',, 'b'].map(function (x,i) { return i+'.'+x })
[ '0.a', , '2.b' ]filter()は穴を削除します。
> ['a',, 'b'].filter(function (x) { return true })
[ 'a', 'b' ]join()は穴、undefined、およびnullを空の文字列に変換します。
> ['a',, 'b'].join('-')
'a--b'
> [ 'a', undefined, 'b' ].join('-')
'a--b'sort()はソート中に穴を保持します。
> ['a',, 'b'].sort() // length of result is 3 [ 'a', 'b', , ]
for-inループは、プロパティキー(これは配列インデックスのスーパーセットです)を正しくリストします。
> for (var key in ['a',, 'b']) { console.log(key) }
0
2apply()は、各穴を値がundefinedの引数に変換します。次の相互作用がこれを示しています。関数f()は、引数を配列として返します。apply()に3つの穴を持つ配列を渡してf()を呼び出すと、後者は3つのundefined引数を受け取ります。
> function f() { return [].slice.call(arguments) }
> f.apply(null, [ , , ,])
[ undefined, undefined, undefined ]つまり、apply()を使用して、undefinedを持つ配列を作成できます。
> Array.apply(null, Array(3)) [ undefined, undefined, undefined ]
apply()は、空の配列の穴をundefinedに変換しますが、穴を含んでいる可能性がある任意配列の穴を埋めるために使用することはできません。たとえば、任意の配列[2]を考えてみましょう。
> Array.apply(null, [2]) [ , ,]
配列には穴が含まれていないため、apply()は同じ配列を返すはずです。代わりに、長さ2の空の配列(中身はすべて2つの穴)を返します。これは、Array()が単一の数値を配列要素としてではなく、配列の長さとして解釈するためです。
すでに見たように、filter()は穴を削除します。
> ['a',, 'b'].filter(function (x) { return true })
[ 'a', 'b' ]任意の配列の穴をundefinedに変換するためのカスタム関数を使用します。
functionconvertHolesToUndefineds(arr){varresult=[];for(vari=0;i<arr.length;i++){result[i]=arr[i];}returnresult;}
関数の使用
> convertHolesToUndefineds(['a',, 'b']) [ 'a', undefined, 'b' ]
Array.isArray(obj)
objが配列の場合、trueを返します。レルム(ウィンドウまたはフレーム)をまたがるオブジェクトを正しく処理します—instanceofとは対照的に(落とし穴:レルム(フレームまたはウィンドウ)をまたぐを参照)。次のセクションでは、配列のプロトタイプメソッドを機能別にグループ化します。各サブセクションでは、メソッドが破壊的(呼び出された配列を変更する)であるか、非破壊的(レシーバーを変更しない。そのようなメソッドは多くの場合、新しい配列を返す)であるかを説明します。
Array.prototype.shift()
インデックス0の要素を削除して返します。後続の要素のインデックスは1ずつデクリメントされます。
> var arr = [ 'a', 'b' ]; > arr.shift() 'a' > arr [ 'b' ]
Array.prototype.unshift(elem1?, elem2?, ...)
指定された要素を配列の先頭に追加します。新しい長さを返します。
> var arr = [ 'c', 'd' ];
> arr.unshift('a', 'b')
4
> arr
[ 'a', 'b', 'c', 'd' ]Array.prototype.pop()
配列の最後の要素を削除して返します。
> var arr = [ 'a', 'b' ]; > arr.pop() 'b' > arr [ 'a' ]
Array.prototype.push(elem1?, elem2?, ...)
指定された要素を配列の末尾に追加します。新しい長さを返します。
> var arr = [ 'a', 'b' ];
> arr.push('c', 'd')
4
> arr
[ 'a', 'b', 'c', 'd' ]apply()(Function.prototype.apply(thisValue, argArray)を参照)を使用すると、配列arr2を別の配列arr1に破壊的に追加できます。
> var arr1 = [ 'a', 'b' ]; > var arr2 = [ 'c', 'd' ]; > Array.prototype.push.apply(arr1, arr2) 4 > arr1 [ 'a', 'b', 'c', 'd' ]
Array.prototype.splice(start, deleteCount?, elem1?, elem2?, ...)
> var arr = [ 'a', 'b', 'c', 'd' ]; > arr.splice(1, 2, 'X'); [ 'b', 'c' ] > arr [ 'a', 'X', 'd' ]
特別なパラメータ値
startは負の値にすることができ、その場合は長さに追加されて開始インデックスが決定されます。したがって、-1は最後の要素を参照します。deleteCountはオプションです。省略した場合(およびそれに続くすべての引数も)、インデックスstart以降のすべての要素が削除されます。この例では、最後から2番目のインデックス以降のすべての要素を削除します。
> var arr = [ 'a', 'b', 'c', 'd' ]; > arr.splice(-2) [ 'c', 'd' ] > arr [ 'a', 'b' ]
これらのメソッドも破壊的です。
Array.prototype.reverse()
配列内の要素の順序を反転し、元の(変更された)配列への参照を返します。
> var arr = [ 'a', 'b', 'c' ]; > arr.reverse() [ 'c', 'b', 'a' ] > arr // reversing happened in place [ 'c', 'b', 'a' ]
Array.prototype.sort(compareFunction?)
配列をソートして返します。
> var arr = ['banana', 'apple', 'pear', 'orange']; > arr.sort() [ 'apple', 'banana', 'orange', 'pear' ] > arr // sorting happened in place [ 'apple', 'banana', 'orange', 'pear' ]
ソートでは値を文字列に変換して比較することに注意してください。つまり、数値は数値的にソートされないことを意味します。
> [-1, -20, 7, 50].sort() [ -1, -20, 50, 7 ]
ソート方法を制御するオプションのパラメータcompareFunctionを指定することで、これを修正できます。次のシグネチャを持ちます。
functioncompareFunction(a,b)
この関数は、aとbを比較して、次を返します。
aがbより小さい場合は、ゼロより小さい整数(例:-1)aがbと等しい場合は、ゼロaがbより大きい場合は、ゼロより大きい整数(例:1)数値の場合、単純にa-bを返すことができますが、これにより数値オーバーフローが発生する可能性があります。それを防ぐには、より冗長なコードが必要になります。
functioncompareCanonically(a,b){if(a<b){return-1;}elseif(a>b){return1;}else{return0;}}
私はネストされた条件演算子が好きではありません。しかし、この場合、コードが非常に簡潔になるため、お勧めしたい誘惑にかられます。
functioncompareCanonically(a,b){returna<b?-1:(a>b?1:0);}
関数の使用
> [-1, -20, 7, 50].sort(compareCanonically) [ -20, -1, 7, 50 ]
文字列の場合、String.prototype.localeCompareを使用できます(文字列の比較を参照)
> ['c', 'a', 'b'].sort(function (a,b) { return a.localeCompare(b) })
[ 'a', 'b', 'c' ]パラメータcompareFunctionは、オブジェクトのソートにも役立ちます。
vararr=[{name:'Tarzan'},{name:'Cheeta'},{name:'Jane'}];functioncompareNames(a,b){returna.name.localeCompare(b.name);}
比較関数としてcompareNamesを使用すると、arrはnameでソートされます。
> arr.sort(compareNames)
[ { name: 'Cheeta' },
{ name: 'Jane' },
{ name: 'Tarzan' } ]次のメソッドは、配列に対してさまざまな非破壊的操作を実行します。
Array.prototype.concat(arr1?, arr2?, ...)
レシーバーのすべての要素の後に、配列arr1のすべての要素などが続く新しい配列を作成します。パラメータの1つが配列でない場合は、要素として結果に追加されます(たとえば、最初の引数の'c')。
> var arr = [ 'a', 'b' ];
> arr.concat('c', ['d', 'e'])
[ 'a', 'b', 'c', 'd', 'e' ]concat()が呼び出される配列は変更されません。
> arr [ 'a', 'b' ]
Array.prototype.slice(begin?, end?)
配列の要素を、beginで始まり、endの要素を含まない新しい配列にコピーします。
> [ 'a', 'b', 'c', 'd' ].slice(1, 3) [ 'b', 'c' ]
endがない場合は、配列の長さが使用されます。
> [ 'a', 'b', 'c', 'd' ].slice(1) [ 'b', 'c', 'd' ]
両方のインデックスがない場合、配列がコピーされます。
> [ 'a', 'b', 'c', 'd' ].slice() [ 'a', 'b', 'c', 'd' ]
いずれかのインデックスが負の場合、配列の長さがそれに追加されます。したがって、-1は最後の要素を参照します。
> [ 'a', 'b', 'c', 'd' ].slice(1, -1) [ 'b', 'c' ] > [ 'a', 'b', 'c', 'd' ].slice(-2) [ 'c', 'd' ]
Array.prototype.join(separator?)
すべての配列要素にtoString()を適用し、結果の間にseparatorの文字列を挿入することで文字列を作成します。separatorが省略されている場合は、','が使用されます。
> [3, 4, 5].join('-')
'3-4-5'
> [3, 4, 5].join()
'3,4,5'
> [3, 4, 5].join('')
'345'join()は、undefinedとnullを空の文字列に変換します。
> [undefined, null].join('#')
'#'配列内の穴も空の文字列に変換されます。
> ['a',, 'b'].join('-')
'a--b'次のメソッドは、配列内の値を検索します。
Array.prototype.indexOf(searchValue, startIndex?)
startIndexから始まる配列でsearchValueを検索します。最初に出現したインデックスを返し、何も見つからない場合は-1を返します。startIndexが負の場合は、配列の長さが追加されます。省略した場合は、配列全体が検索されます。
> [ 3, 1, 17, 1, 4 ].indexOf(1) 1 > [ 3, 1, 17, 1, 4 ].indexOf(1, 2) 3
厳密な等価性(等価演算子:===と==を参照)が検索に使用されます。つまり、indexOf()はNaNを見つけることができません。
> [NaN].indexOf(NaN) -1
Array.prototype.lastIndexOf(searchElement, startIndex?)
startIndexから始まる配列で、searchElementを逆方向に検索します。最初に出現したインデックスを返し、何も見つからない場合は-1を返します。startIndexが負の場合は、配列の長さが追加されます。省略した場合は、配列全体が検索されます。厳密な等価性(等価演算子:===と==を参照)が検索に使用されます。
> [ 3, 1, 17, 1, 4 ].lastIndexOf(1) 3 > [ 3, 1, 17, 1, 4 ].lastIndexOf(1, -3) 1
反復メソッドは、関数を使用して配列を反復処理します。非破壊的な3種類の反復メソッドを区別します:検査メソッドは主に配列の内容を観察します。変換メソッドはレシーバーから新しい配列を導出します。そして、縮小メソッドはレシーバーの要素に基づいて結果を計算します。
このセクションで説明する各メソッドは、次のようになります。
arr.examinationMethod(callback,thisValue?)
このようなメソッドは、以下のパラメータを取ります。
callback は最初のパラメータで、呼び出す関数です。検査メソッドに応じて、コールバックは boolean を返すか、何も返しません。それは以下のシグネチャを持ちます。
functioncallback(element,index,array)
element は callback が処理する配列要素、index は要素のインデックス、そして array は examinationMethod が呼び出された配列です。
thisValue は callback 内の this の値を設定できます。Array.prototype.forEach(callback, thisValue?)
配列の要素を反復処理します
vararr=['apple','pear','orange'];arr.forEach(function(elem){console.log(elem);});
Array.prototype.every(callback, thisValue?)
この例では、配列内のすべての数値が偶数であるかどうかをチェックします。
> function isEven(x) { return x % 2 === 0 }
> [ 2, 4, 6 ].every(isEven)
true
> [ 2, 3, 4 ].every(isEven)
false配列が空の場合、結果は true になります(そして callback は呼び出されません)。
> [].every(function () { throw new Error() })
trueArray.prototype.some(callback, thisValue?)
この例では、配列に偶数があるかどうかをチェックします。
> function isEven(x) { return x % 2 === 0 }
> [ 1, 3, 5 ].some(isEven)
false
> [ 1, 2, 3 ].some(isEven)
true配列が空の場合、結果は false になります(そして callback は呼び出されません)。
> [].some(function () { throw new Error() })
falseforEach() の潜在的な落とし穴の1つは、ループを早期に中断するために break や類似のものをサポートしていないことです。それが必要な場合は、some() を使用できます。
functionbreakAtEmptyString(strArr){strArr.some(function(elem){if(elem.length===0){returntrue;// break}console.log(elem);// implicit: return undefined (interpreted as false)});}
some() は、中断が発生した場合に true を返し、それ以外の場合は false を返します。これにより、(for ループでは少しトリッキーな)反復が正常に終了したかどうかによって異なる反応をすることができます。
変換メソッドは入力配列を取り、出力配列を生成します。その間、コールバックが出力の生成方法を制御します。コールバックは、検査と同じシグネチャを持ちます。
functioncallback(element,index,array)
Array.prototype.map(callback, thisValue?)
各出力配列要素は、入力要素に callback を適用した結果です。例えば
> [ 1, 2, 3 ].map(function (x) { return 2 * x })
[ 2, 4, 6 ]Array.prototype.filter(callback, thisValue?)
出力配列には、callback が true を返す入力要素のみが含まれます。例えば:
> [ 1, 0, 3, 0 ].filter(function (x) { return x !== 0 })
[ 1, 3 ]縮約の場合、コールバックは異なるシグネチャを持ちます。
functioncallback(previousValue,currentElement,currentIndex,array)
パラメータ previousValue は、コールバックによって以前に返された値です。コールバックが最初に呼び出されるとき、2つの可能性があります(説明は Array.prototype.reduce() の場合です。reduceRight() との違いは括弧内に記載されています)。
initialValue が提供されています。この場合、previousValue は initialValue であり、currentElement は最初の配列要素になります(reduceRight:最後の配列要素)。initialValue は提供されていません。この場合、previousValue は最初の配列要素であり、currentElement は2番目の配列要素になります(reduceRight:最後の配列要素と最後から2番目の配列要素)。縮約メソッドは2つあります。
Array.prototype.reduce(callback, initialValue?)
左から右に反復処理し、前に概説したようにコールバックを呼び出します。メソッドの結果は、コールバックによって最後に返された値です。この例では、すべての配列要素の合計を計算します。
functionadd(prev,cur){returnprev+cur;}console.log([10,3,-1].reduce(add));// 12
単一の要素を持つ配列で reduce を呼び出した場合、その要素が返されます。
> [7].reduce(add) 7
空の配列で reduce を呼び出した場合、initialValue を指定する必要があります。そうしないと、例外が発生します。
> [].reduce(add) TypeError: Reduce of empty array with no initial value > [].reduce(add, 123) 123
Array.prototype.reduceRight(callback, initialValue?)
reduce() と同様に機能しますが、右から左に反復処理します。多くの関数型プログラミング言語では、reduce は fold または foldl (左畳み込み)として知られており、reduceRight は foldr (右畳み込み)として知られています。
reduce メソッドの別の見方は、それが n 項演算子 OP を実装しているということです。
OP1≤i≤n xi
二項演算子 op2 の一連の適用を介して
(...(x1 op2 x2) op2 ...) op2 xn
これは前のコード例で発生したことです。JavaScript の二項プラス演算子を介して、配列の n 項和演算子を実装しました。
例として、次の関数を介して 2 つの反復方向を調べてみましょう。
functionprintArgs(prev,cur,i){console.log('prev:'+prev+', cur:'+cur+', i:'+i);returnprev+cur;}
予想どおり、reduce() は左から右に反復処理します。
> ['a', 'b', 'c'].reduce(printArgs) prev:a, cur:b, i:1 prev:ab, cur:c, i:2 'abc' > ['a', 'b', 'c'].reduce(printArgs, 'x') prev:x, cur:a, i:0 prev:xa, cur:b, i:1 prev:xab, cur:c, i:2 'xabc'
そして、reduceRight() は右から左に反復処理します。
> ['a', 'b', 'c'].reduceRight(printArgs) prev:c, cur:b, i:1 prev:cb, cur:a, i:0 'cba' > ['a', 'b', 'c'].reduceRight(printArgs, 'x') prev:x, cur:c, i:2 prev:xc, cur:b, i:1 prev:xcb, cur:a, i:0 'xcba'
JavaScript の一部のオブジェクトは配列のように見えますが、配列ではありません。これは通常、インデックス付きアクセスと length プロパティを持っているが、配列メソッドがないことを意味します。例としては、特殊変数 arguments、DOM ノードリスト、および文字列などがあります。配列のようなオブジェクトと汎用メソッドは、配列のようなオブジェクトを操作するためのヒントを提供します。
配列 arr を反復処理するには、2つのオプションがあります。
配列を反復処理するために for-in ループ (for-in を参照)を使用しないでください。これは値ではなく、インデックスを反復処理します。また、継承されたものも含め、通常のプロパティのキーを含みます。