インデックス付きコレクション

この節では、インデックス値により順序付けされたデータのコレクションを紹介します。配列、Array オブジェクト、TypedArray オブジェクトなどの配列用の構造体があります。

Array オブジェクト

配列は名前やインデックスで参照できる値からなる順序集合です。例えば、emp という配列を作成し、従業員番号と従業員の名前を対応付けることができます。つまり、emp[1] が従業員番号 1 、emp[2] が従業員番号 2 、のようになります。

JavaScript は明確な配列データ型を持っていません。しかし、アプリケーションで配列として機能する定義済みの Array オブジェクトとそのメソッドを利用できます。Array オブジェクトには配列の結合、反転、ソートなど様々な方法で配列を操作するメソッドがあります。また、配列の長さを特定するプロパティや、正規表現で使用するプロパティなどがあります。

配列の生成

以下の文は同じ配列を生成します。

let arr = new Array(element0, element1, ..., elementN)
let arr = Array(element0, element1, ..., elementN)
let arr = [element0, element1, ..., elementN]

element0, element1, ..., elementN は配列要素になる値から構成されるリストです。これらの値が指定されると、この配列の要素はそれらの値に初期化されます。配列の length プロパティは引数の数に設定されます。

角括弧による構文は「配列リテラル」または「配列初期化子」と呼ばれます。この構文はその他の形式による配列作成よりも短いため、一般的に好まれる方法です。詳細については、配列リテラルをご覧ください。

長さがゼロではないが項目のない配列を作成するには、以下の方法が使用できます。

// これはこ...
let arr = new Array(arrayLength)

// ...このような結果になります
let arr = Array(arrayLength)


// これも同じ効果があります
let arr = []
arr.length = arrayLength

注: 上記のコードでは、arrayLengthNumber(数値)である必要があります。さもないと、(指定した値の)単一の要素を持つ配列が生成されます。arr.length を呼び出すと arrayLength が返されますが、実際には配列は空要素 (undefined) で構成されます。この配列で for...in ループを実行しても、配列の要素は返されません。

上記のように新規に定義した変数に割り当てるだけでなく、新規または既存のオブジェクトのプロパティに配列を割り当てることができます。

let obj = {}
// ...
obj.prop = [element0, element1, ..., elementN]

// または
let obj = {prop: [element0, element1, ...., elementN]}

単一の要素で配列を初期化しようとして、その要素が Number である場合、角括弧の構文を使用する必要があります。単一の Number 値が Array() コンストラクタや関数に渡されると、単一の数値要素としてではなく、arrayLength として解釈されます。

let arr = [42]       // 42 という数の要素を
                     // 1 個だけ持つ配列が作られます。

let arr = Array(42)  // 要素がなく、arr.length が
                     // 42 に設定された配列が作られます。
                     //
                     // 以下のコードと同様です。
let arr = []
arr.length = 42

N の値が小数部分がゼロではない実数である場合、Array(N) を呼び出すと、結果は RangeError になります。以下の例ではこの動作を示します。

let arr = Array(9.3)   // RangeError: Invalid array length

任意のデータ型の単一の要素を持つ配列を作成したければ、配列リテラルを使用する方が安全です。あるいは、単一の要素を追加する前に空の配列を作成しましょう。

ES2015 から単一の要素を持つ配列を生成するために Array.of 静的メソッドを使用することができます。

let wisenArray = Array.of(9.3)   // wisenArray は 1 つの要素 9.3 だけを持つ配列

配列要素の参照

要素はプロパティでもあるので、プロパティアクセサーを使ってアクセスすることができます。以下の配列を定義するとします。

let myArray = ['Wind', 'Rain', 'Fire']

要素のインデックスは 0 から始まるので、配列の 1 番目の要素を myArray[0]、2 番目の要素を myArray[1] と呼ぶことができます。

注: プロパティアクセサーを使用して、オブジェクトのように配列の他のプロパティにアクセスすることもできます。

let arr = ['one', 'two', 'three']
arr[2]          // three
arr['length']   // 3

配列へのデータ追加

要素に値を割り当てることで配列にデータを追加することができます。例えば、

let emp = []
emp[0] = 'Casey Jones'
emp[1] = 'Phil Lesh'
emp[2] = 'August West'

注: 上記のコードで配列演算子(角括弧)内に非整数値を指定すると、配列要素ではなく配列を表すオブジェクトのプロパティとして作成されます。

var arr = [];
arr[3.4] = 'Oranges';
console.log(arr.length);                // 0
console.log(arr.hasOwnProperty(3.4));   // true

配列を作成するときにも、データを追加することができます。

let myArray = new Array('Hello', myVar, 3.14159)
// または
let myArray = ['Mango', 'Apple', 'Orange']

配列の長さの理解

実装レベルでは、JavaScript の配列は、配列のインデックスをプロパティ名として使用して、その要素を標準的なオブジェクトのプロパティとして格納します。

length プロパティは特別です。これは常に最終要素のインデックス +1 を返します(次の例では、'Dusty' のインデックスは 30 なので、cats.length30 + 1 を返します)。

JavaScript の配列のインデックスは 0 から始まることを覚えておいてください。これは、length プロパティは配列に格納されている最大のインデックスより 1 つ多い値になるということです。

var cats = [];
cats[30] = ['Dusty'];
console.log(cats.length); // 31

length プロパティに値を割り当てることもできます。

格納されているアイテムの数より小さい値を設定すると、配列は切り捨てられます。すなわち、0 に設定すると完全に配列を空にします。

let cats = ['Dusty', 'Misty', 'Twiggy'];
console.log(cats.length); // 3

cats.length = 2;
console.log(cats); // ログに "Dusty, Misty"  - Twiggy は削除される

cats.length = 0;
console.log(cats); // ログに [ ] 、配列 cats は空になる

cats.length = 3;
console.log(cats); // ログに [ <3 個の空スロット> ]

配列の反復処理

よく行われるのは配列に含まれる値に対し、それぞれの値について、なんらかの処理を行うことです。これを行う一番簡単な方法は次のとおりです。

let colors = ['red', 'green', 'blue'];
for (var i = 0; i < colors.length; i++) {
  console.log(colors[i]);
}

配列内の要素がいずれも真偽値としては false に評価されないことがわかっている場合 ― 例えば配列が DOM ノードのみで構成されている場合などには、例のように、より効率的な表現を使用できます。

let divs = document.getElementsByTagName('div');
for (var i = 0, div; div = divs[i]; i++) {
  /* div に対して何らか処理をする */
}

この例では、配列の長さのチェックに掛かるオーバーヘッドを回避しています。そしてより便利に使えるように、ループの反復のたびに div 変数に現在の項目を代入するようにしています。

配列を反復処理する別の方法として forEach() メソッドがあります。

let colors = ['red', 'green', 'blue'];
colors.forEach(function(color) {
  console.log(color);
});
// red
// green
// blue

あるいは、ES2015 のアロー関数式を forEach の引数にしてコードを短縮することもできます。

let colors = ['red', 'green', 'blue'];
colors.forEach(color => console.log(color)); 
// red
// green
// blue

forEach に渡される関数では、その関数への引数に配列の要素が渡されて、配列内の各項目ごとに 1 回ずつ実行されます。値が割り当てられていない要素は forEach ループで反復されません。

配列定義の際に省略された要素は、forEach によって反復処理されるときには現れませんが、配列要素に undefined が割り当てられている場合は現れることに注意してください。

let array = ['first', 'second', , 'fourth']

array.forEach(function(element) {
  console.log(element)
})
// first
// second
// fourth

if (array[2] === undefined) { 
  console.log('array[2] is undefined')  // true
} 

array = ['first', 'second', undefined, 'fourth']

array.forEach(function(element) {
  console.log(element)
})
// first
// second
// undefined
// fourth

JavaScript では、配列の要素は標準的なオブジェクトプロパティとして保存されるので、for...in ループを使って JavaScript 配列を反復処理するのはお勧めできません。というのも、通常の要素とすべての列挙可能なプロパティが現れるからです。

配列のメソッド

Array オブジェクトには以下のようなメソッドがあります。

concat() は 2 つの配列を結合し、新しい配列を返します。

let myArray = new Array('1', '2', '3')
myArray = myArray.concat('a', 'b', 'c')
// myArray は ["1", "2", "3", "a", "b", "c"] になる

join(delimiter = ',') は配列のすべての要素を文字列に結合します。

var myArray = new Array('Wind', 'Rain', 'Fire');
var list = myArray.join(' - '); // list は "Wind - Rain - Fire" になる

push() は 1 つ以上の要素を配列の最後に追加し、その新しい配列の長さを返します。

var myArray = new Array('1', '2');
myArray.push('3'); // myArray は ["1", "2", "3"] になる

pop() は配列から最後の要素を取り除き、その要素を返します。

let myArray = new Array('1', '2', '3')
let last = myArray.pop()
// myArray は ["1", "2"] に、last は "3" になる

shift() は配列から最初の要素を取り除き、その要素を返します。

let myArray = new Array('1', '2', '3')
let first = myArray.shift()
// myArray は ["2", "3"]に、first は "1" になる

unshift() は 1 つ以上の要素を配列の先頭に追加し、その新しい配列の長さを返します。

let myArray = new Array('1', '2', '3')
myArray.unshift('4', '5')
// myArray は ["4", "5", "1", "2", "3"] になる

slice(start_index, upto_index) は配列の一部を抽出し、新しい配列を返します。

let myArray = new Array('a', 'b', 'c', 'd', 'e')
myArray = myArray.slice(1, 4)  // インデックス 1 から始め、インデックス 3 まですべての要素を
                               // 展開して、[ "b", "c", "d"] を返す

splice(index, count_to_remove, addElement1, addElement2, ...) は要素を配列から取り除き、(必要に応じて)置き換えます。

let myArray = new Array('1', '2', '3', '4', '5')
myArray.splice(1, 3, 'a', 'b', 'c', 'd') 
// myArray は ["1", "a", "b", "c", "d", "5"] になる
// このコードは、インデックス 1 の要素("2" のある場所)から始まり、
// 3 つの要素を削除して、そこに後続のすべての要素を挿入します。

reverse() は配列の中の要素をその場で反転させます。配列の最初の要素が最後に、最後の要素が最初になります。配列への参照を返します。

let myArray = new Array('1', '2', '3')
myArray.reverse()
// 配列要素が入れ替えられ、myArray = ["3", "2", "1"] になる

sort() は配列の要素をその場でソートし、その配列の参照を返します。

let myArray = new Array('Wind', 'Rain', 'Fire')
myArray.sort() 
// 配列がソートされ、myArray = ["Fire", "Rain", "Wind"] になる

sort() は要素を比較する方法を指定するための、コールバック関数を引数として取ることがあります。

sort メソッドを始めとしコールバック関数を引数として取る以下のメソッドは 反復メソッド (iterative method) と呼ばれ、何らかの形で配列全体を反復処理します。それぞれが任意で thisObject と呼ばれる第二引数を受け取ります。thisObject が与えられた場合、これがコールバック関数の本体内で this キーワードの値になります。与えられなかった場合は、関数が明示的なオブジェクトコンテキストの外で呼び出された場合と同様に、this はアロー関数がコールバックとして使用された場合にはグローバルオブジェクト(window)を参照し、通常の関数の場合には undefined になります。

コールバック関数は、配列の要素 2 つを引数として呼び出されます。

以下の関数は 2 つの値を比較して、3 つの値のうち 1 つを返します。

つまり、以下の例は文字列の最後の文字で並べ替えをします。

let sortFn = function(a, b) {
  if (a[a.length - 1] < b[b.length - 1]) return -1;
  if (a[a.length - 1] > b[b.length - 1]) return 1;
  if (a[a.length - 1] == b[b.length - 1]) return 0;
}
myArray.sort(sortFn) 
// 配列がソートされ、myArray = ["Wind","Fire","Rain"] になる
  • このソートシステムにより ab より小さいとされた場合、-1(または、任意の負の数)を返します。
  • このソートシステムにより ab より大きいとされた場合、1(または、任意の正の数)を返します。
  • ab が等値と見なされる場合、0 を返します。

indexOf(searchElement[, fromIndex]) は配列から searchElement を検索します。そして、最初に一致した位置のインデックスを返します。

let a = ['a', 'b', 'a', 'b', 'a'] 
console.log(a.indexOf('b'))     // 1 がログに出力される

// 最初から最後への検索を試してみる
console.log(a.indexOf('b', 2))  // 3 がログに出力される
console.log(a.indexOf('z'))     // 'z' は見つからないので -1 がログに出力される

lastIndexOf(searchElement[, fromIndex])indexOf のように動作しますが、最後の要素から開始して前方に検索します。

let a = ['a', 'b', 'c', 'd', 'a', 'b']
console.log(a.lastIndexOf('b'))     // 5 がログに出力される

// 最後から最初への検索を試してみる
console.log(a.lastIndexOf('b', 4))  // 1 がログに出力される
console.log(a.lastIndexOf('z'))     // -1 がログに出力される

forEach(callback[, thisObject]) はすべての配列アイテムにコールバック関数 callback を実行し、undefined を返します。

let a = ['a', 'b', 'c']
a.forEach(function(element) { console.log(element) })
// 順番にそれぞれのアイテムをログに出力する

map(callback[, thisObject]) はすべての配列アイテムごとにコールバック関数 callback を実行し、戻り値からなる新しい配列を返します。

let a1 = ['a', 'b', 'c']
let a2 = a1.map(function(item) { return item.toUpperCase() })
console.log(a2) // ['A', 'B', 'C'] がログに出力される

filter(callback[, thisObject]) はコールバック関数 callback が true を返すアイテムからなる新しい配列を返します。

let a1 = ['a', 10, 'b', 20, 'c', 30]
let a2 = a1.filter(function(item) { return typeof item === 'number'; })
console.log(a2)  // [10, 20, 30] がログに出力される

every(callback[, thisObject]) はコールバック関数 callback が配列内のすべてのアイテムで true を返す場合に true を返します。

function isNumber(value) {
  return typeof value === 'number' 
}
let a1 = [1, 2, 3]
console.log(a1.every(isNumber))  // true がログに出力される
let a2 = [1, '2', 3] 
console.log(a2.every(isNumber))  // false がログに出力される

some(callback[, thisObject]) はコールバック関数 callback が配列内の少なくとも一つのアイテムで true を返す場合に true を返します。

function isNumber(value) {
  return typeof value === 'number'
}
let a1 = [1, 2, 3]
console.log(a1.some(isNumber))  // true がログに出力される
let a2 = [1, '2', 3]
console.log(a2.some(isNumber))  // true がログに出力される
let a3 = ['1', '2', '3']
console.log(a3.some(isNumber))  // false がログに出力される

reduce(callback[, initialValue]) は、配列の各値に対して callback(accumulator, currentValue[, currentIndex[, array]]) を適用し、項目のリストを一つの値に減らすことを目的としています。reduce 関数は、コールバック関数によって返された最終的な値を返します。

initialValue が指定された場合は、initialValue を第 1 引数の値として、配列の最初の項目の値を第 2 引数の値としてコールバックが呼び出されます。

initialValue が指定されていない場合、コールバックのの最初の 2 つの引数の値は、配列の最初と 2 番目の要素になります。後続のすべての呼び出しで、最初の引数の値は前の呼び出しでコールバックが返した値になり、2 番目の引数の値は配列の次の値になります。

コールバックが処理対象の項目のインデックスにアクセスする必要がある場合は、配列全体にアクセスするときに、オプションの引数として利用できます。

let a = [10, 20, 30]
let total = a.reduce(function(accumulator, currentValue) { return accumulator + currentValue }, 0)
console.log(total) // 60 がログに出力される

reduceRight(callback[, initialValue])reduce() のように機能します。しかし最後の要素から適用を開始します。

reducereduceRight もある意味では配列の反復メソッドです。要素列を単一の値に還元するために、再帰的に 2 つの値を組み合わせるアルゴリズムにこれらのメソッドを使用してください。

多次元配列

配列をネストすることができます、つまり配列要素として配列を含めることができることを意味します。JavaScript の配列の特徴を活かして、多次元配列を生成できます。

以下のコードでは 2次元配列を作成しています。

let a = new Array(4)
for (let i = 0; i < 4; i++) {
  a[i] = new Array(4)
  for (let j = 0; j < 4; j++) {
    a[i][j] = '[' + i + ', ' + j + ']'
  }
}

この例では、次のテーブル行を持つ配列を作成しています。

Row 0: [0, 0] [0, 1] [0, 2] [0, 3]
Row 1: [1, 0] [1, 1] [1, 2] [1, 3]
Row 2: [2, 0] [2, 1] [2, 2] [2, 3]
Row 3: [3, 0] [3, 1] [3, 2] [3, 3]

配列を使用して他のプロパティを格納する

配列は、オブジェクトのように関連する情報を格納するために使用することもできます。

const arr = [1, 2, 3];
arr.property = "value";
console.log(arr.property);  // "value" がログに出力される

配列と正規表現

配列が正規表現と文字列との間の一致結果である場合、その配列は一致についての情報を提供するプロパティと要素を返します。RegExp.exec()String.match()String.split() による戻り値がこうした配列となります。正規表現とともに配列を使用する際の情報については、当ガイドの正規表現の章をご覧ください。

配列用のオブジェクトを利用する

document.getElementsByTagName() によって返される NodeList や、関数本体内で利用できる arguments オブジェクトのように、表面上は配列のようにふるまう JavaScript オブジェクトがありますが、これらはメソッドすべてを共有してはいません。例えば、arguments オブジェクトには length 属性がありますが、forEach() メソッドは実装されていません。

配列状のオブジェクトに対して配列メソッドを直接呼び出すことはできません。

function printArguments() {
  arguments.forEach(function(item) {  // TypeError: arguments.forEach is not a function 
    console.log(item);
  });
}

これを行うには、Function.prototype.call() を使って間接的に呼び出します。

function printArguments() {
  Array.prototype.forEach.call(arguments, function(item) {
    console.log(item);
  });
}

配列のプロトタイプメソッドは、配列と同様の方法で文字に逐次アクセスできるため、文字列にも使用できます。

Array.prototype.forEach.call('a string', function(chr) {
  console.log(chr)
})

型付き配列

JavaScript の型付き配列は配列用のオブジェクトで、未加工のバイナリーデータにアクセスする仕組みをもたらします。ご存知のように、Array オブジェクトは動的に拡大、縮小し、JavaScript におけるいかなる値でも保持することができます。JavaScript エンジンは最適化を行うため、これらの配列は高速に機能します。しかし、オーディオやビデオ操作といった機能が追加され、WebSocket を使い未加工のデータにアクセスするなど、Web アプリケーションはさらにパワフルなものとなってきました、そのため JavaScript コードが型付き配列内の未加工バイナリーデータを手早く簡単に操作できれば有益である場合がよくある、ということが明らかになってきました。

バッファとビュー : 型付き配列のアーキテクチャ

最大の柔軟性と効率性を達成するため、JavaScript 型付き配列の実装をバッファビューに分離しました。バッファ(ArrayBuffer オブジェクトによる実装)はデータのかたまりを表すオブジェクトです。語るほどのフォーマットはなく、データの中身にアクセスするためのメカニズムを提供しません。バッファに含まれるメモリーにアクセスするには、ビューを使用する必要があります。ビューはデータを実際の型付き配列に変換するコンテキスト — つまり、データ型、開始位置のオフセット、要素数 — を提供します。

Typed arrays in an ArrayBuffer

ArrayBuffer

ArrayBuffer は汎用的な固定長のバイナリーデータバッファを表すために使用されるデータ型です。ArrayBuffer の内容は直接操作できません。かわりに、型付き配列ビューか特定のフォーマットでバッファを表す DataView を生成し、それらをバッファの内容の読み書きに使用します。

型付き配列ビュー

型付き配列ビューは自己記述的な名前を持っていて、そのすべてが Int8, Uint32, Float64 などといったよく見られる数値型用のビューを提供しています。Uint8ClampedArray という 1 つ特別な型付き配列ビューがあります。これは、0〜255 の範囲に値を固定します。これは、例えば、Canvas のデータ処理に便利です。

値の範囲 サイズ (バイト数) 説明 Web IDL 型 同等の C 型
Int8Array -128 から 127 1 8 ビット 2 の補数方式の符号付き整数値 byte int8_t
Uint8Array 0 から 255 1 8 ビット 符号なし整数値 octet uint8_t
Uint8ClampedArray 0 から 255 1 8 ビット 符号なし整数値 (切り詰め) octet uint8_t
Int16Array -32768 から 32767 2 16 ビット 2 の補数方式の符号付き整数値 short int16_t
Uint16Array 0 から 65535 2 16 ビット 符号なし整数値 unsigned short uint16_t
Int32Array -2147483648 から 2147483647 4 32 ビット 2 の補数方式の符号付き整数値 long int32_t
Uint32Array 0 から 4294967295 4 32 ビット 符号なし整数値 unsigned long uint32_t
Float32Array 1.2×10-38 から 3.4×1038 4 32 ビット IEEE 浮動小数点数 (7 桁の有効数字 例:1.1234567) unrestricted float float
Float64Array 5.0×10-324 から 1.8×10308 8 64 ビット IEEE 浮動小数点数 (16 桁の有効数字 例:1.123...15) unrestricted double double
BigInt64Array -263 から 263-1 8 64 ビット 2 の補数方式の符号付き整数値 bigint int64_t (signed long long)
BigUint64Array 0 から 264-1 8 64 ビット 符号なし整数値 bigint uint64_t (unsigned long long)

詳細については、JavaScript 型付き配列と様々な TypedArray オブジェクトに関するリファレンスをご覧ください。