制御フローとエラー処理
JavaScript は、特に制御フロー文についてはコンパクトな文のセットに対応しており、アプリケーションに多様な対話的機能を組み込むために利用することができます。この節ではこれらの文の概要を説明します。
JavaScript リファレンスには、この章で紹介する文についての完全な詳細が載っています。また、JavaScript のコードではセミコロン (;
) 文字で文を区切ります。
あらゆる JavaScript の式は、文でもあります。式に関する詳細については式と演算子を参照してください。
ブロック文
最も基本的な文は「ブロック文」で、文のグループ化に使います。ブロックは、一組の波括弧で区切られます。
{ statement1; statement2; // … statementN;}
例
ブロック文は一般に制御フロー文 (if
,for
,while
) で用いられます。
while (x < 10) { x++;}
ここでは{ x++; }
がブロック文となります。
条件文
条件文は、指定した条件が真の場合に実行する命令の集まりです。JavaScript はif...else
とswitch
の 2 つの条件文に対応しています。
if...else 文
if
を使用すると、論理条件がtrue
の場合に文を実行することができます。任意のelse
節を使用すると、条件がfalse
の場合にも文を実行することができます。
if
文は次のように使用します。
if (condition) { statement1;} else { statement2;}
ここでcondition
は、true
またはfalse
と評価される任意の式にすることができます。(true
とfalse
の評価の説明については、Boolean を参照してください。)
条件がtrue
と評価された場合、statement1
が実行されます。そうでなければ、statement2
が実行されます。statement1
とstatement2
は、入れ子になったif
文も含めて、任意の文にすることができます。
以下のように、else if
を使用した文を組み合わせて、複数の条件を順番に検査することもできます。
if (condition1) { statement1;} else if (condition2) { statement2;} else if (conditionN) { statementN;} else { statementLast;}
複数の条件がある場合、true
と評価された最初の論理条件のみが実行されます。複数の文を実行するには、ブロック文 ({ /* … */ }
) 内にグループ化します。
ベストプラクティス
一般的に、常にブロック文を使用するのが優れた方法です。特にif
文を入れ子にしたコードで有効です。
if (condition) { // 条件が true のときに実行される文 // …} else { // 条件が false のときに実行される文 // …}
一般的に、if...else
の条件にx = y
のような代入文を使用しないのがベストプラクティスです。
if (x = y) { /* ここに文が来る */}
しかし、たまにそのような書き方をしたくなるような場合もあるでしょう。例えば、while
のドキュメントには代入を条件として使用という節があり、知っておくべき、従うべき一般的なベストプラクティスの構文の案内があります。
偽値
以下の値はfalse
と評価されます(また、偽値とも呼ばれます)。
false
undefined
null
0
NaN
- 空の文字列 (
""
)
上記以外の—オブジェクトを含む—すべての値は、条件文に渡されるとtrue
と評価されます。
メモ:プリミティブな論理値のtrue
とfalse
を、Boolean
オブジェクトの true や false という値と混同しないでください。
例:
const b = new Boolean(false);if (b) { // この条件は true に評価される}if (b == true) { // この条件は false に評価される}
例
次の例で、関数checkData
はText
オブジェクトに含まれている文字数が 3 である場合にtrue
を返し、そうでない場合はアラートを表示してfalse
を返します。
function checkData() { if (document.form1.threeChar.value.length === 3) { return true; } else { alert( `Enter exactly three characters. ${document.form1.threeChar.value} is not valid.`, ); return false; }}
switch 文
switch
文を使うと、プログラムは式を評価し、その式の値をcase
ラベルと照合します。一致すると、プログラムはそのラベルに関連付けられた文を実行します。
switch
文は次のようになります。
switch (expression) { case label1: statements1; break; case label2: statements2; break; // … default: statementsDefault;}
JavaScript は上記の switch 文を次のように評価します。
- プログラムは最初に、式の値に一致するラベルを持つ
case
節を探し、その節に制御を移して、関連付けられた文を実行します。 - 一致するラベルがない場合、プログラムは省略可能な
default
節を探します。- 存在する場合は
default
節に制御を移し、関連付けられた文を実行します。 default
節が見つからない場合、プログラムはswitch
文の末尾の後に続く文から実行を再開します。- (慣例により、
default
節は最後の節に置きますが、そうしなければいけないわけではありません。)
- 存在する場合は
break 文
break
文はオプションで、それぞれのcase
節と関連付けられ、該当する文が実行されるとプログラムがswitch
から抜け出し、switch
の次の文から実行が継続されることを保証します。break
が省略されると、プログラムはswitch
文の内部の実行を続けます(そして、次のcase
を順番に評価します)。
例
次の例では、fruitType
が"Bananas"
と評価された場合、プログラムではcase "Bananas"
と値が一致するので、それに関連付けられた文を実行します。break
文に出くわすと、プログラムはswitch
から抜けて、switch
の後に続く文を実行します。break
を省略すると、case "Cherries"
の文も実行されます。
switch (fruitType) { case "Oranges": console.log("Oranges are $0.59 a pound."); break; case "Apples": console.log("Apples are $0.32 a pound."); break; case "Bananas": console.log("Bananas are $0.48 a pound."); break; case "Cherries": console.log("Cherries are $3.00 a pound."); break; case "Mangoes": console.log("Mangoes are $0.56 a pound."); break; case "Papayas": console.log("Papayas are $2.79 a pound."); break; default: console.log(`Sorry, we are out of ${fruitType}.`);}console.log("Is there anything else you'd like?");
例外処理文
throw
文を使用して例外を発生させ、try...catch
文を使用して例外を処理することができます。
例外の型
JavaScript では、ほぼどのようなオブジェクトでも例外として投げることができます。とはいえ、必ずしも投げられるオブジェクトすべてが同等に作られているわけではありません。数値や文字列をエラーとして投げる方法がよく用いられますが、こうした用途のために特別に作られた例外データ型を使用した方がより効率的な場合が多いです。
throw 文
throw
文は、例外を投げる(発生させる)ために使用します。例外を発生させるには、投げたい値を含む式を指定してください。
throw expression;
特定の型の式だけではなく、あらゆる式を投げることができます。下記のコードでは、さまざまな型の例外を発生させています。
throw "Error2"; // 文字列型throw 42; // 数値型throw true; // 論理型throw { toString() { return "これはオブジェクトです!"; },};
try...catch 文
try...catch
文は検査したい文のブロックを指定し、さらに発生した例外に対する 1 つ以上の対処方法を指定します。例外が発生すると、try...catch
文がそれを受け取ります。
try...catch
文は 1 つのtry
ブロックと、try
ブロック内で例外が発生した場合に何をするかを指定する分を含むcatch
ブロックから成ります。
要するに、成功した場合に実行したいtry
ブロックと、失敗した場合に制御を移行させたいcatch
ブロックで構成されています。try
ブロック内(もしくはtry
ブロック内から呼び出された関数内)のいずれかの文が例外を投げると、制御はすぐにcatch
ブロックに移ります。try
ブロックで例外が発生しなかった場合、catch
ブロックはスキップされます。finally
ブロックはtry
およびcatch
ブロックを実行した後に実行しますが、try...catch
文の後に続く文より先に実行されます。
次の例ではtry...catch
文を使用しています。この例では渡された値に基づいて、配列から月の名前を取り出す関数を実行します。値に対応する月の数字 (1
–12
) がない場合は'InvalidMonthNo'
という値を持つ例外が発生し、catch
ブロックの中の文はmonthName
という変数に'unknown'
という値を設定します。
function getMonthName(mo) { mo--; // 月の数字を配列のインデックスに合わせる (0 = Jan, 11 = Dec) const months = [ "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", ]; if (months[mo]) { return months[mo]; } else { throw "InvalidMonthNo"; // ここで throw キーワードが使われている }}try { // 実行を試みる文 monthName = getMonthName(myMonth); // この関数が例外を投げる場合がある} catch (e) { monthName = "unknown"; logMyErrors(e); // 例外オブジェクトをエラーハンドラーに渡す}
catch ブロック
catch
ブロックを用いることで、try
ブロックで発生しうるすべての例外を扱うことができます。
catch (exception) { statements}
catch
ブロックには識別子 (上記の構文におけるexception
) を指定し、これがthrow
文で指定された値を保持します。この識別子は、発生した例外についての情報を得るために使います。
JavaScript はcatch
ブロックに入るときにこの識別子を作成します。識別子はcatch
ブロックの区間だけ存続します。つまり、catch
ブロックの実行が終わると、その識別子はもう使えなくなります。
例えば、次のコードは例外を投げます。例外が生じると、制御がcatch
ブロックに移ります。
try { throw "myException"; // 例外を生成} catch (err) { // ここには例外を扱う文が入る logMyErrors(err); // 例外オブジェクトをエラーハンドラーに渡す}
メモ:catch
ブロック内でコンソールにエラーをログ出力する場合は、console.log()
よりもconsole.error()
がデバッグ目的では推奨されています。これはメッセージをエラーとして書式化し、ページによって生成されたエラーメッセージの一覧に追加します。
finally ブロック
finally
ブロックは、try
およびcatch
ブロックの実行「後」に実行される文が入ります。また、finally
ブロックの中のコードはtry…catch…finally
に続く文が実行される「前」に実行されます。
また、finally
ブロックは例外が発生するかどうかにかかわらず、実行されるということに注意することも大切です。また、例外が発生したら、finally
ブロック内の文は発生した例外がcatch
ブロックで処理されなくても実行されます。
finally
ブロックを使用することで、例外発生時に適切にスクリプトを停止させることができます。例えば、スクリプトで使用していたリソースを解放しなければならない場合などです。
次の例ではファイルを開き、そのファイルを使用する文を実行します(サーバー側 JavaScript ではファイルにアクセスできます)。ファイルを開いている間に発生すると、スクリプトが停止する前にfinally
ブロックでそのファイルを閉じます。ここでfinally
を使用することで、エラーが発生した場合であってもファイルが開かれたままにならないことを保証します。
openMyFile();try { writeMyFile(theData); // ここでエラーが投げられる可能性がある} catch (e) { handleError(e); // エラーを受け取り、それを処理する} finally { closeMyFile(); // 常にリソースが閉じられる}
finally
ブロックが値を返す場合、その値はtry
およびcatch
ブロックのreturn
文にかかわらずtry…catch…finally
全体が生成する返値になります。
function f() { try { console.log(0); throw "bogus"; } catch (e) { console.log(1); // この返値は、finally ブロックが // 完了するまで保留となる return true; console.log(2); // ここまで到達しない } finally { console.log(3); return false; // 直前の "return" が上書きされる console.log(4); // ここまで到達しない } // ここで "return false" が実行される console.log(5); // ここまで到達しない}console.log(f()); // 0, 1, 3, false
finally
ブロックによる返値の上書きは、catch
ブロック内で発生した、または再発生した例外にも適用されます。
function f() { try { throw "bogus"; } catch (e) { console.log('caught inner "bogus"'); // この throw 文は finally ブロックが // 完了するまで保留になる throw e; } finally { return false; // 直前の "throw" が上書きされる } // ここで "return false" が実行される}try { console.log(f());} catch (e) { // ここには到達しない // f() を実行した際、`finally` ブロックが false を返し、 // 上記の `catch` の中にある `throw` を上書する console.log('caught outer "bogus"');}// Logs:// caught inner "bogus"// false
入れ子の try...catch 文
1 つ以上のtry...catch
文を入れ子にすることができます。
内側のtry
ブロックにcatch
ブロックがない場合は次のようになります。
finally
ブロックを置く必要があります。そして、- 外側の
try...catch
文のcatch
ブロックがエラーの照合先としてチェックされます。
詳しくは、try...catch
の中の入れ子になった try ブロックを参照してください。
Error オブジェクトの活用
エラーの種類に応じて、name
やmessage
プロパティを使ってより詳細なメッセージが得られるようにすることができます。
name
はError
のクラス全般 (例えばDOMException
やError
) を表し、一方message
は通常、エラーオブジェクトを文字列に変換したものより簡潔なメッセージを表します。
独自の例外を発生させて、これらのプロパティを有効に活用したい場合 (catch
ブロックで独自の例外とシステムの例外とを区別したくない場合など)、Error
コンストラクターが使えます。
例えば、次のようにします。
function doSomethingErrorProne() { if (ourCodeMakesAMistake()) { throw new Error("メッセージ"); } else { doSomethingToGetAJavaScriptError(); }}try { doSomethingErrorProne();} catch (e) { // `console.error()` を実際に使ってログを出力してみます console.error(e.name); // 'Error' console.error(e.message); // 'メッセージ'、または JavaScript のエラーメッセージをログ出力}