JavaScript入門 制御構文(繰り返し)
JavaScriptの盛業構文の繰り返し(for文、while文、bleack文、continue文、label文)の使用方法について解説
while文
while文は、条件式が真のときに処理を繰り返し、偽が取得されたときに処理を抜けます。
構文
while( 条件式 ){
whileブロック 条件式が真の場合繰り返し処理されます。
}
while文の使用例
let i = 0;
while ( i < 5 ){
console.log( i );
i++;
}
結果
>0
>1
>2
>3
>4
無限ループに陥った時
while文では、条件式が真の結果を返すとループが継続されるため、while( true )のように、場合によっては無限にループが繰り返されます。無限ループが発生した場合、JavaScriptの処理が終わらずブラウザがフリーズしてしまうので、ブラウザのタブを一度閉じて終わらせましょう。
for文
for文を使って繰り返し(ループ)処理を記述する場合は、「初期化処理」「ループ継続のための条件式」「ループごとの最後に実行される式」をセミコロン(;)で区切ります。
構文
for( 初期化処理;ループ継続の条件式;ループごとの最後に実行される式 ){
ループ継続の条件式が真の場合、繰り返し処理されるコードを記述
}
使用例
for( let i = 0; i < 5; i++ ){
console.log( i );
}
この例では、最初に初期化処理としてlet i = 0を実行しています。次に、条件式 i < 5が評価され、iが5未満のときにブロック{}内の処理が実行されます。ブロック{}内のコードの実行が終わると、ループごとの最後に実行される式i++で、iの値に1を加算しています。再び、条件式(i < 5)が評価されるという処理の流れが繰り返されます。
for文の式の省略
for文の3つの式は、それぞれ省略することができます。for(;;)のように記述した場合には、while文の条件式にtrueを直接記述したwhile(true)と同じように無限ループになります。特定の条件でループを抜けるようにしたい場合は、break文と組み合わせて使います。
for( ;; ){
const rand = Math.random();
console.log( rand );
if( rand > 0.5 ){
//0.5より大きい場合にfor文を抜ける
break;
}
}
配列とfor文
配列とは複数の値を保持できるオブジェクトです。保持されている値にいはそれぞれ0から始まるインデックスが付与されています。
- numArray.lengthによって、配列の要素数をforループの上限値としています。
- index++によって、indexの値は0,1,2…とインクリメントしていくため、indexを使って配列から値を取得します。numArray[ index ]+10で元の配列の値に対して10を加算して、それを代入演算子によって配列の同じインデックスの要素に新しい値をセットします。
const numArray = [ 10, 20, 30 ];
for( let index = 0; index < numArray.length; index++ ){
//配列の値をコンソールに出力
numArray[ index ] = numArray[ index ] + 10;
}
console.log( numArray );
>[20,30,40]
for in文
for…in文は、オブジェクトの要素に対して繰り返し処理を行うときに使います。
構文
- key:オブジェクトのプロパティが1つずつ渡されます
- obj:オブジェクトや配列を設定できます。
fot( const key in obj ){
オブジェクトの各プロパティに対する繰り返し処理
}
const fruits = { apple: "りんご",banana:"バナナ",orange:"オレンジ" };
for( const key in fruits ){
console.log( `キー[$[key]] 値:[${fruits[key]}]`);
}
>キー[apple] 値:[りんご]
>キー[banana] 値:[バナナ]
>キー[orange] 値:[オレンジ]
配列も同様にfor…in文を使って繰り返し処理を行うことができます。しかし、for …in文では渡される要素の順番が担保されていません。そのため、基本的に配列にはfor文もしくは、for…of文を使うと良いでしょう
for in文と列挙可能性
for in文の繰り返し処理の対象となるプロパティとならないプロパティがあります。取得されるプロパティを「列挙可能プロパティ」と呼び、その設定を「列挙可能性(enumerable)」と呼びます。列挙可能性が有効なプロパティはfor…in文の取得対象となり、無効なプロパティは取得対象となりません。
なぜ、有効プロパティと無効プロパティがあるのでしょうか?例として、配列の場合、配列要素数を取得するlengthというプロパティがあります。これは配列要素ではないためfor in文では取得ありません。そのため、lengthプロパティはデフォルト列挙可能性が無効になっています。
//配列定義
const arry = [ "リンゴ" , "バナナ" ];
//配列のキーをループで取得
for( const key in arry ){
console.log( key );
}
//配列の中身を確認
console.log( arry );
列挙可能性の設定は、プロパティ記述子(ディスクリプタ)というオブジェクトのプロパティとして保持されています。
プロパティ記述子内の列挙可能性の確認方法
const プロパティ記述子 = Reflect.getOwnPropertyDescriptor( オブジェクト名,"プロパティ" );
const プロパティ記述子 = Object.getOwnPropertyDescriptor( オブジェクト名,"プロパティ" );
サンプル
const obj = {prop : "値"}
const propDesc = Reflect.getOwnPropertyDescriptor(obj,"prop");
console.log( propDesc );
列挙可能性(enumerable)にはtrueが設定されているため、for in文の対象となります。
プロパティ記述子の設定方法
const プロパティ記述子 = Reflect.defineProperty( 設定対象のオブジェクト,"プロパティ","プロパティ記述子" );
const プロパティ記述子 = Object.defineProperty( 設定対象のオブジェクト,"プロパティ","プロパティ記述子" );
サンプル
const obj = { prop1: "これは列挙課の正プロパティです" };
Reflect.defineProperty( obj,"prop2",{ value: "これは列挙可能性プロパティではありません。" , enumerable: false} );
for( const key in obj ){
console.log(key,obj[key]);
}
>これは列挙課の正プロパティです
このように、列挙可能性を無効にしたプロパティは、for in文の列挙対象になりません。
for in文とSysbol
ES6で追加されたSymbol型のプロパティも、for in文の列挙対象になりません。(列挙可能性がtrueであっても対象になりません。)
const s = Symbol();
const obj = { [s]: "Symbol型のプロパティのため、列挙対象となりません。"};
for( const key in obj ){
console.log(key,obj[key]);
}
>
for of文
for of文では、反復可能オブジェクトに対する繰り返し処理を記述できます。主に、配列(Array)やMap,Setに対して繰り返し処理ができます。
構文
- value:反復可能オブジェクトの種類によって渡される値が変わります。配列やSetの場合は値が渡されますが、Mapの場合はプロパティと値が対になった配列が[ プロパティ,値 ]という形式で渡されます。
- iterable:反復可能オブジェクト(配列,Map、Set、String、argumentsなど)を設定できます。
for( const value of iterable ){
反復可能オブジェクトの各要素に対する繰り返し処理
}
サンプル
const fruits = ["りんご","バナナ","オレンジ"];
for( const value of fruits ){
console.log( value );
}
>りんご
>バナナ
>オレンジ
一方、Mapオブジェクトに対してfor of文で繰り返し処理を行った場合には、プロパティと値が対で格納されている配列が渡されます。
const map = new Map;
map.set( "apple","りんご" ); //キーと値のペアをMapに登録
map.set( "banana","バナナ" );
for ( const row of map ){
//配列の0番目にキー、1番目に値が格納されている
console.log( row[ 0 ], row[ 1 ] );
}
>apple りんご
>banana バナナ
分割代入によってキーと値を個別の変数に直接取得
const map = new Map;
map.set( "apple","りんご" ); //キーと値のペアをMapに登録
map.set( "banana","バナナ" );
for ( const [key, value] of map ){
//分割代入構文により、任意の変数名でキーと値を受け取り、ループ内で使用可能
console.log( key, value );
}
>apple りんご
>banana バナナ
オブジェクトをfor of文で使う
オブジェクトリテラル{}で定義したオブジェクトは、反復可能オブジェクトではないため、for …of文では使えません。しかし、Objectの静的メソッドを使うことで、for of文で利用できる配列や反復可能オブジェクトへ変換できます。
プロパティの配列を取得する場合
オブジェクトのプロパティに対して繰り返しよりを行いたい場合は、Object.keysを使ってプロパティの配列を取得します。
構文
const プロパティの配列 = Object.keys( オブジェクト );
const fruits = { apple:"リンゴ",banana:"バナナ" };
const props = Object.keys( fruits );
console.log( props );
>[ "apple" , "banana" ]
//for of文でプロパティの配列ループ
for ( const prop of props ){
console.log( prop,fruits[ prop ] );
}
>apple リンゴ
>banana バナナ
値の配列を取得する場合
オブジェクトの値に対して繰り返し処理を行いたい場合は、Object.valuesを使って値の配列を取得します。
構文
const 値の配列 = Object.values( オブジェクト );
オブジェクトの値を配列で取得
const fruits = { apple:"リンゴ",banana:"バナナ" };
const values = Object.values( fruits );
console.log( values );
>[ "リンゴ" , "バナナ" ]
//for of文でプロパティの配列ループ
for ( const value of values){
console.log( value );
}
>リンゴ
>バナナ
プロパティと値のペアを配列で取得する場合
オブジェクトのプロパティと値を繰り返し処理で利用したい場合は、Object.entriesを使ってプロパティと値のペアを配列として取得します。
プロパティと値をペアで保持する配列を取得
const fruits = { apple:"リンゴ",banana:"バナナ" };
const entries = Object.entries( fruits );
console.log( entries );
>[[ apple,"リンゴ" ],[banana,"バナナ"]]
//for of文でプロパティと値のペアの配列をループ
for ( const entry of entries){
console.log( entry[0], entry[1] );
}
>リンゴ
>バナナ
また、Object.entriesを使う場合、プロパティと値が[ apple,”リンゴ” ]のような配列に変換されるため、Mapと同様に分割代入を使った記法を利用できます。
for( const [ key , value] of Object.entries( { apple: "リンゴ", banana: "バナナ"} ) ){
console.log (key,value);
}
for文、for in文、for of文の使い分け
・配列ループ
for文またはfor of文を使いましょう。for of文を使う場合はインデックスが取得できないため、インデックスが必要な場合は、for文を使います。
・オブジェクト(Object)ループ
オブジェクトは、for文ではループ処理ができません。プロパティのみは、for in文。もしくは、Object.valuesやObject.entriesを使います。
break文
break文を使うことで、ブロック処理から抜けて後続の処理をすることができます。break文は主に、while文、for文、switch文のブロック内で使います。また、label文と組み合わせることで、if文的な使用も可能です。
let i = 0;
while ( true ){ //無限ループ
if ( i > 3 ){
break; //iが3より大きい場合にwhileループを抜ける
}
console.log( i );
i++;
}
>0
>1
>2
>3
continue文
continue文がwhile文やfor文のような繰り返し処理中で呼び出された場合に、現在のループ中の処理を中断して、次のループをブロックの最初から実行します。
let i = 0;
while ( i < 8 ){
i++;
if( i % 2 ){ //奇数のときにtrue、偶数のときにfalse
continue;
}
console.log( i ); //奇数のときにはこの行は実行sれない
}
>2
>4
>6
>8
label文
label文は、ブロック{}に対してラベル(任意の名前)を付与します。continue文やbreak文でラベルを指定することで、そのブロックに対してcontinueやbreakの操作を行います。continue文と組み合わせて使う場合、for文やwhile文などの繰り返し処理のブロックに付与したラベルのみ指定できます。一方、break文と組み合わせて使う場合は、繰り返し処理以外のブロックに付与したラベルも指定できます。
構文
- labelName:ブロック{}に対して付与するラベル(任意の名前)です。
labelName: { break labelName; }
labelName: if(・・・) { break labelName; }
labelName: switch(・・・) { case・・・;break labelName; }
labelName: for(・・・){ continue | break ] labelName; }
labelName: while(・・・){ continue | break ] labelName; }
たとえば、複数のループ処理が入れ子になっている状態で、すべてのループを抜ける処理を実装したい場合は、label文とbreak文を使います。
const str = [ "a","b","c","d" ];
const num = [ 10, 20, 30 ]
alphabet:
for( let i = 0; i < str.length; i++ ){
numeric:
for( let j = 0; j < num.length; j++ ){
console.log(`アルファベット : [ ${ str[ i ] } ]`,` 数値: [ ${ num[j] } ] `);
if( str[ i ] === "b" && num[ j ] === 30 ){
break alphabet; //親のループに対するbreak処理
}
}
}
block:{
const rand = Math.random();
if( rand > 0.5 ){
break block;
}
console.log( "0.5より大きいときはスキップされます。" );
}
console.log( "blockの後継処理です。" );