【JavaScript】 forEachの使い方と使うべきでないケース
更新日:2021/08/02
JavaScriptのforEachは配列を順番に処理することができる非常に便利なメソッドです。
しかし特性を理解していないと使用できないことがあります。
また便利だからと頼りすぎるのもよくありません。
ここではforEachの使い方と、forEachを使用すべきでないケースを解説します。
forEachが使える条件
僕もそうでしたが、JavaScriptに慣れていないと、forEachを使ってみたけれど使えなかったということがよくあります。
まず知っておくべきことは、forEachはArrayオブジェクトのメソッドだということです。
Arrayオブジェクトは配列の実体です。
そのため、次のように配列に対してのみ実行できます。
forEachは配列のみで使用できる
const array = [ 1 , 2 , 3];
array.forEach( value => { /* なんらかの処理 */ } );
配列リテラルから直接呼び出す
[ 1 , 2 , 3 ].forEach( value => { /* なんらかの処理 */ } );
よくやってしまう間違いは、自分で定義したオブジェクトに対してforEachを使用することです。
使用すると、TypeErrorになります。
間違い:オブジェクトに対してforEach
const obj = {
value1:10 , value2:100 , value3:1000
};
obj.forEach( value =>{ /* なんらかの処理 */ } );
// TypeError: obj.forEach is not a function
オブジェクトにforEachメソッドが定義されていないのが原因です。
オブジェクトに対してforEachを使用する場合、Object.entries()などで一度配列に変換します。
詳しくは次のページを読んでみてください。
forEachの使い方
forEachの使い方について、お伝えしていきます。
forEachメソッドの定義
ArrayオブジェクトのforEachメソッドは配列の要素を、引数として指定された関数に一つずつ渡します。
関数は受け取った要素を使用して、処理をおこないます。
forEachメソッドの定義
配列.forEach( 関数 , this値 );
配列.forEach( アロー関数 , this値 );
※this値 は省略可能です。詳しくはforEachとthis値で解説します。
具体的には、次のように使用します。
具体例
// 無名関数を引数として渡す
[ 1 , 2 , 3].forEach( function(value){ console.log(value); } );
[ 1 , 2 , 3].forEach( value => console.log(value) );
// 変数として定義した関数を引数として渡す
const func = function(value){ console.log(value); };
[ 1 , 2 , 3].forEach( func );
const arrowfunc = value => console.log(value);
[ 1 , 2 , 3].forEach( arrowfunc );
コールバック関数の定義
forEachメソッドに渡して呼び出される関数、つまりコールバック関数は、3つの引数を受け取ります。
コールバック関数の形式
コールバック関数( 要素値 , 要素インデックス , 元になる配列 ){ /* なんらかの処理 */ }
後方の引数は省略可能なので、全部で3つのパターンに展開できます。
コールバック関数の3つのパターン
コールバック関数( 要素値 ){ /* なんらかの処理 */ }
コールバック関数( 要素値 , 要素インデックス ){ /* なんらかの処理 */ }
コールバック関数( 要素値 , 要素インデックス , 元になる配列 ){ /* なんらかの処理 */ }
要素値は、配列から順番に抜き出した要素です。
要素インデックスは、現在処理している要素値のインデックスです。
元になる配列は、要素値が格納されている配列です。
使用方法のヒント
具体的な使用例を提示するのは難しいので、ここではコールバック関数の各パターンを使用する上でのヒントとなる例をお伝えします。
なお、引数が一つのみのパターンは省略します。
引数が二つのパターン
コールバック関数で配列のインデックス情報が必要なときは、引数が二つのパターンを使用します。
次の例は、配列のインデックスが奇数か偶数かを判断して、それぞれ新しい配列に文字列を格納しています。
インデックスが奇数・偶数かで分ける
const a = [ 100 , 200 , 300 ];
const even = []; // 偶数
const odd = []; // 奇数
a.forEach( (e,index) =>
index % 2 === 0 ? odd.push( index + ":" + e ) : even.push( index + ":" + e )
);
console.log( even ); // [ "1:200" ]
console.log( odd ); //[ "0:100", "2:300" ]
また、何らかの理由でインデックスが欠落する可能性がある場合もあります。
配列のインデックスが欠落するケース
const a = [ 100 , 200 , 300 ];
delete a[1]; // インデックス1を削除
a.forEach( (e,index) =>console.log( index + ":" + e ) );
// 0:100
// 2:300
上の例では、インデックス1をdeleteで削除しています。
この場合forEachは、インデックス1に対してコールバック関数を呼び出しません。
コールバック関数内のコードが配列の順番に依存し、インデックスが欠落する可能性がある場合は、2番目の引数をチェックする必要がないか検討する必要があります。
引数が三つのパターン
コールバック関数で元の配列の内容を変更したいなど、元の配列の情報が必要なときは引数が三つのパターンを使用します。
次の例は、要素値とインデックス値を掛け算して、その結果を元の配列に上書きしています。
元の配列に上書き
const a = [ 1 , 2 , 3];
a.forEach( (e,index,array) =>array[index] = e * index );
console.log( a ); // [ 0, 2, 6 ]
上のようなコードは、実際には三つ目の引数を指定せずに、次のように配列aを直接参照することが多いです。
const a = [ 1 , 2 , 3];
a.forEach( (e,index) => a[index] = e * index );
次のように変数として定義したときには、三つ目の引数が活躍してくれます。
const callBack = (e,index,array) =>array[index] = e * index;
const a = [ 1 , 2 , 3];
a.forEach( callBack );
forEachとthis値
ArrayオブジェクトのforEachメソッドは、二つ目の引数として、コールバック関数内でthis値として扱う値を渡すことができます。
forEachメソッドの定義
配列.forEach( 関数 , this値 );
配列.forEach( アロー関数 , this値 ); // 想定した動作はしません!
function関数をコールバック関数として渡した場合、関数内のコードで二つ目の引数として渡された値をthis値として使用できます。
forEachにthis値を渡す
const callBack = function(e,index){
a[index] = e * index * this.value; // this.value = 100
};
const a = [ 1 , 2 , 3];
const thisValue = { value:100</span> }; // this値として渡す値
a.forEach( callBack ,thisValue );
console.log( a ); // [ 0, 200, 600 ]
しかしコールバック関数としてアロー関数を渡した場合、this値を受け取ることができません。
forEachにthis値を渡す(アロー関数)
const callBack = (e,index)=>
a[index] = e * index * this.value; // this.value = undefined
const a = [ 1 , 2 , 3];
const thisValue = { value:100</span> }; // this値として渡す値
a.forEach( callBack ,thisValue );
console.log( a ); // [ NaN, NaN, NaN ]
そのため、2番目の引数は無効となります。
アロー関数については、次のページを読んでみてください。
■【JavaScript】 アロー関数は何者!?かっこいいだけじゃない!
forEachを使用すべきでないケース
forEachメソッドは配列内の全てのように対して、処理やチェックなどをおこなうことができます。
そのため、さまざまな用途で使用できます。
しかしArrayオブジェクトには、配列の要素に対して特定の処理をおこなうメソッドがいくつか用意されています。
それらのメソッドはforEachメソッドでも実現可能です。
しかし、用意されているメソッドを使用したほうが効率的です。
ここで紹介しているいくつかのメソッドは、次の記事で詳しく紹介しています。
■【JavaScript】 forEach/map/filter/reduceを根本的に理解する
また、forEachメソッドは値を返しませんが、値を返すメソッドに置き換えることでメソッドチェーンを構築することができます。
そのため、スッキリとしたコードを記述することができます。
メソッドチェーンについては、こちらを参照してみてください。
■【JavaScript】 メソッドチェーンを本質的に理解する
配列要素からサイズが同じ配列を作成する場合
配列の要素に手を加え、新たな配列を作成する場合はmapメソッドを使用します。
mapは、コールバック関数が返した値を、内部で作成した新しい配列にセットします。
全ての値に対して処理が終わると、新しい配列を返します。
次の例は、mapメソッドを使用しないパターンです。
mapメソッドを使用しないパターン
const a = [ 100 , 200 , 300 ];
const newArray = [ ];
a.forEach( e =>newArray.push( e * 3 ) );
console.log( newArray ); // [ 300, 600, 900 ]
次は、mapメソッド使用したパターンです。
mapメソッド使用パターン
const a = [ 100 , 200 , 300 ];
const newArray = a.map( e => e * 3 );
console.log( newArray ); // [ 300, 600, 900 ]
4行から3行に減っただけに見えますが、forEachをmapに変更したことで、新しい配列を作成しているという意図を他者に伝えることができます。
mapメソッドは配列を返すので、Arrayオブジェクトのメソッドをチェーンすることができます。
配列要素から一部を抜き出す場合
条件に一致する要素を別の配列にまとめたいときは、filterを使用します。
filterは、コールバック関数がtrueを返すとき、内部で作成した新しい配列にその値をセットします。
全ての値に対してチェックが終わると、新しい配列を返します。
forEachの例は挙げません。
filter使用パターン
const data = [ 1 , 2 , 3 , 4 ];
// 奇数を抽出
const odd = data.filter( value => value % 2 === 1 );
// 偶数を抽出
const even = data.filter( value => value % 2 === 0 );
console.log( odd ); // [ 1, 3 ]
console.log( even ); // [ 2, 4 ]
filterメソッドは配列を返すので、Arrayオブジェクトのメソッドをチェーンすることができます。
要素の妥当性をチェックする
全ての要素が、条件を満たしているか確認する場合は、everyを使用します。
everyは、コールバック関数が全てtrueを返すとき、trueを返します。
forEachの例は挙げません。
every使用パターン
const data = [ 1 , 2 , 3 , 4 ];
const result = data.every( value => typeof value === "number");
console.log( result ); // true
const data2 = [ "a" , "b" , 3 , "d" ];
const result2 = data2.every( value => typeof value === "string");
console.log( result2 ); // false
everyは真偽値を返すため、メソッドチェーンをおこなうことができません。
求める値があるかをチェックする
配列内に条件に一致する値が一つでもあるかチェックしたいときは、someを使用します。
someは、コールバック関数が最初にtrueを返したとき、trueを返します。
forEachの例は挙げません。
some使用パターン
const data = [ 1 , 2 , 3 , 4 ];
const result = data.some( value => value === 3);
console.log( result ); // true
const result2 = data.some( value => typeof value === "string");
console.log( result2 ); // false
someは真偽値を返すため、メソッドチェーンをおこなうことができません。
値同士を順番に演算して一つの結果を得る
全ての値の合計など、値同士になんらかの計算をおこない、最終的に結果を一つ得たいときはreduceを使用します。
reduceは、計算途中の値と要素の値をコールバック関数に渡します。
コールバック関数は、それらの値を使用して計算をおこない、結果を返します。
全ての要素について処理が終わったら、最終結果を返します。
forEachの例は挙げません。
reduce使用パターン:合計を求める
const data = [ 1 , 2 , 3 , 4 ];
const result = data.reduce( (a,b) => a + b , 0);
console.log( result ); // 10
reduceの戻り値は、コールバック関数の戻り値に依存します。
そのため、メソッドチェーンできる場合とできない場合があります。
次のreduceの例は配列を返すので、Arrayオブジェクトのメソッドとチェーンできます。
reduce使用パターン:配列を返す
const result2 = data.reduce( (a,b) => {
a.push( (a.length === 0 ? 0 : a[a.length-1]) + b );
return a;
}, [ ]).join( "," );
console.log( result2 ); // 1,3, 6,10
reduceは、要素同士を演算して結果を求めたいとき、ほとんどのケースで使用できます。
更新日:2021/08/02
関連記事
スポンサーリンク
記事の内容について
こんにちはけーちゃんです。
説明するのって難しいですね。
「なんか言ってることおかしくない?」
たぶん、こんなご意見あると思います。
裏付けを取りながら記事を作成していますが、僕の勘違いだったり、そもそも情報源の内容が間違えていたりで、正確でないことが多いと思います。
そんなときは、ご意見もらえたら嬉しいです。
掲載コードについては事前に動作確認をしていますが、貼り付け後に体裁を整えるなどをした結果動作しないものになっていることがあります。
生暖かい視線でスルーするか、ご指摘ください。
ご意見、ご指摘はこちら。
https://note.affi-sapo-sv.com/info.php
このサイトは、リンクフリーです。大歓迎です。