制御構造

JavaScriptで利用できる制御構造を以下に示す。

条件の成否

JavaScript の制御構造で、偽(false)として振舞うのは以下のいずれかである。

undefined未定義(関数や変数など)
null
false真偽値の false
0数値の0で±0を含む
NaN非数値(Not A Number)
""長さ0の文字列

これら以外の値は制御構造ですべて true 扱いとなる。 以下の説明での「偽」は false として振舞ういずれかを意味し、 「真」はそれ以外を意味する。

if

条件分岐には if を使う。

if ()   [ else 2 ]

指定した の評価結果が真であれば後続する を評価し、偽であれば else 節があった場合のみ 2 を評価する。

文の部分には、複数の文を { } で括ったブロックを書ける(以下の制御構造でも同様)。

条件演算子

if...else の短絡表記のように使え、この演算子自体が値を返す。

 ? 1 : 2

が真の場合に 1 を、 そうでない場合に 2 を返す。

while

while は、条件が成り立つあいだの繰り返しを形成する。

while () 

の部分を毎回評価して、それが真を返す間 を繰り返し評価する。

do...while

前述 while 同様繰り返しを形成するが、 繰り返し条件の判定を本体の後ろで行なうのが do...while である。

do  while ();

最低1回は必ず行なうべき繰り返し処理の記述に向いている。

for

for は繰り返しを形成する。

for (初期化式; 条件式; 末尾式) 

繰り返しそのものに入る前に 初期化式 を1回だけ評価する。 そのあと を評価する前に 条件式 を評価し、これが真だった場合に を評価し、その直後に 末尾式 を評価する。

次の例は10から0までのカウントダウンを行なう処理を for で記述したものである。

for (var i=10; i>0; i--) {	// iはforを抜けても有効
  console.log(i);
}
console.log(i);			// i=0 が残っている
for (let j=10; j>0; j--) {	// jはfor文内のブロックスコープ変数
  console.log(j);
}
console.log(j);			// 未定義エラー

このように、初期化式 部分を let 宣言にするとブロックスコープになる。以下の for の別形式についても同様である。

for...in

for の第2の書式は繰り返し可能な要素を持つ集合から 一つずつ添字(プロパティ)を取り出して順次変数に代入して繰り返す。

for (変数宣言 in 集合) 

配列とオブジェクトの添字・プロパティを取り出しつつ繰り返す例を示す。

array = ['foo', 'bar', 'baz'];
for (let sub in array) console.log(sub);
=> 0
=> 1
=> 2			// 添字が返るので値を取り出すには array[sub] とする

obj = {foo: 'hoge', bar: 'hero', baz: 'fuga'};
for (let key in obj) console.log(key);
=> foo
=> bar
=> baz		// プロパティが返る。取り出すには obj[key] とする。

上の例では順序よくプロパティ(添字)が出てきたが、 言語仕様では取り出される順番が規定されていない。 また、配列の要素を取り出す場合、0から始まる整数添字以外のプロパティも 出てくるため配列の要素のみを順序よく取り出す目的には向いていない。 配列の要素を順次取り出すには以下のようにすべきである。

for (let i=0; i < array.length; i++) {
  // array[i] を参照した処理
}

for...of

繰り返し可能オブジェクトから値を取り出して繰り返す for である。

for (変数宣言 of 集合) 

配列の値を取り出しつつ繰り返す例を示す。

array = ['foo', 'bar', 'baz'];
for (let sub of array) console.log(sub);
=> foo
=> bar
=> baz

continue

continue はブロック内で用いて、繰り返しブロックの途中であっても、ブロックの末尾まで処理を飛ばす。

continue [ ラベル ];

while, do...while, for のブロックで continue に遭遇するとその一番内側のブロックの末尾に飛ぶ。 多重ループで、外側のループを次の周回に飛ばしたいときはループに入る 直前にラベルを付けておき、continue にラベルを指定する。

loop_i:
for (let i=0; i<10; i++) {
  for (let j=0; j<10; j++) {
    if (なんらかの条件)
      continue loop_i;			// 外側ループの次の周回へ
  }
}

break

break はループブロックや、次で説明する switch の処理ブロックを抜け、ブロックの次の処理に移ることを指示する。

break [ ラベル ];

多重ループからの抜け出しのラベル指定は continue の場合と同様である。

switch...case

switch...case は注目する1つの値に応じた条件分岐を行なう。

switch () { ブロック }

指定した に応じ ブロック にしたがった処理分岐を行なう。ブロックの部分には 任意個の「case句」と1つの「default句」が書ける。

// month になんらかの数値が入っていると仮定する
days = 0;
switch (month) {
case 1: case 3: case 5: case 7:case 8: case 10:  case 12:
    days += 1	// monthが 1, 3, 5, 7, 8, 10, 12 ならここに処理が移る
case 4: case 6: case 9: case 11:	// 上からも処理は流れて来る
    days += 2;	// monthが 4, 6, 9, 11 ならここに処理が移る
case 2:		// month===2 ならここに処理が移る
    days += 28;
    break;	// break でswitchブロックを抜ける
default:	// 以上いずれでもなければ default 句に処理が来る
    days = undefined;  
}
console.log('days='+days);

with

with は、後続するブロックにおいて変数のスコープを変更する。

with (オブジェクト) 

たとえば次のようにすると Math オブジェクトの参照を Math. 前置なしで行なえる。

with (Math) {
  theta = PI/4
  x = cos(theta)
  y = sin(theta)
  r = hypot(x, y)	// sqrt(x**2 + y**2)
}

曖昧性を招くためあまり推奨されない。

try...catch, throw

プログラムそのものの欠陥ではなく、 ネットワークトラブルやディスク容量溢れによる書き込み不能など プログラムが動くときの環境によって発生するエラーのことを例外 という。例外が発生すると通常はプログラムがそこで中断されるが、 あらかじめ例外が起きたときの対処を盛り込んでおくことができる。 これには try 構文を用いる。

try 実行ブロック catch (変数) catchブロック
try 実行ブロック finally finallyブロック
try 実行ブロック catch (変数) catchブロック  finally finallyブロック

実行ブロック の進行中に例外が発生したときに、 発生した例外を catch 節の変数で受け取り、その対処を catchブロック で行なう。finally 節を指定した場合は catch 節の有無や例外発生の有無に関らず、finally 節が評価される。

実行環境で発生する例外だけでなく、 プログラム中で意図的に例外を発生させることもでき、それには throw を使う。try...catch, throw の組み合わせを用いると、終了条件の複数ある(多重)ループから抜けたときにどのように抜けたかによる場合分けを簡潔に書ける。

try {
    while (条件) {
	if (なんらかの条件)
	    throw 例外1;	// ある条件を満たしたら途中でループを抜ける
    }
} catch (e) {
    console.log(e)		// eの値によって場合分けできる
} finally {
  必ず行なうべき終了処理
}

throw の捕捉は、関数呼び出しを越えても可能である。