uhyohyo.net

JavaScript初級者から中級者になろう

九章第八回 例外処理

このページの最終更新日:

例外とは

今回は例外の解説です。例外とは何かというと、簡単にいうとエラーのことです。皆さんも今までに経験があるかもしれませんが、エラーが出るとプログラムはそこで強制的に終了してしまいます。しかし、それだと困る場面があります。そこで、エラーが発生しても対応できる方法があります。

throw文

そこでまず解説するのが、throw文です。throwとは英語で「投げる」という意味です。throwで何かが投げられると、エラーが発生したことになります。


var a=3;
throw a;
console.log(a);

この例では、aを投げています。throwでエラーが発生したのでそこでプログラムが終了し、その後のconsole.logは実行されません。なお、別に投げるのは変数である必要はなく、式なら何でも投げることができます。例えば、throw 3;としても構いません。

このようにして、エラーを発生させることができました。エラーはthrowで何を投げても発生します。それでは、何を投げるのがよいのでしょうか。

Errorオブジェクト

ここで登場するのがErrorオブジェクト、つまりErrorのインスタンスで、その名の通りエラーを表すオブジェクトです。throw文ではこれを投げるのが普通です。実際、処理系によって発生するエラー(プログラム中のthrow文以外によって発生するエラー)で投げられるのは全てErrorオブジェクトのインスタンスです(場合によっては、Errorを継承したオブジェクトのインスタンス)。

Errorのインスタンスにはmessageというプロパティがあり、これがエラーメッセージを表します。なので、自分で作ったErrorオブジェクトを投げるときもエラーメッセージを設定してあげましょう。


var err = new Error();
err.message = "エラーが発生しました。";
throw err;

実際、これを実行するとコンソールに次のようなメッセージが表示されるでしょう。


Uncaught Error: エラーが発生しました。

また、エラーメッセージはいちいちmessageプロパティに代入するのではなく、次のようにコンストラクタの引数で設定することができます。


throw new Error("エラーが発生しました。");

これは言うまでもなく、コンストラクタErrorの働きによってエラーメッセージがmessageプロパティに代入されています。もしErrorを自分で作るならば、


function Error(mes){
  this.message = mes;
}

という感じですね。

try-catch文

さて、これでエラーの発生のさせ方は分かりました。しかし、ただエラーを出すだけでは例外処理とはいえません。

今まで見てきたように、通常エラーが発生したら実行は止まります。しかし、JavaScriptでエラーの発生を検知して処理するという方法があります。それは、try-catch文です。これは次のように書きます。


try{
  処理1
}
catch(e){
  処理2
}

この文はtryブロックとcatchブロックの2つの部分からなることが分かります。では、この文がどのように実行されるのかを説明します。

まずtryの中の処理1が実行されます。その中でエラーが起きなければ、この文の処理は終了です。処理2は実行されません。

一方、処理1でエラーが起きた場合、通常通り処理1はそこで中断されます。そして、処理はcatch中の処理2に移行します。throw(投げる)したエラーをcatch(受け止める)するという感じです。

処理2はcatch(e){ }の中に書いてありますが、この(e)とは何でしょう。実は、括弧の中に書くのは変数名です。ここではeでなくても構いません好きな名前をつけましょう。処理1の中でエラーが起きて処理2が実行されるとき、エラーで投げられたものはここで指定した変数に代入されます。これは前述の通り、大抵はErrorオブジェクトですね。この変数というのは引数名と同じようなものだと思ってください。投げられたものが代入されるための新しい変数を作ってあげることになります。

それでは試してみましょう。


try{
  throw new Error("ぎゃーーーー");
  console.log("ここは実行されません");
}catch(e){
  console.log("エラーが発生しました!!!!!");
  console.log(e);
}

これを実行すると、「エラーが発生しました!!!!!」というハイテンションなログに続けて「ぎゃーーーー」というエラー内容が表示されるはずです。

これは、まずtryブロック(処理1)が実行され、throwにより中断され処理2に移行したことが分かります。throwがないバージョンと動作を比較してみるのもよいでしょう。

もうひとつ、こんな例も見てみましょう。


try{
  console.log(a);
  console.log("ここは実行されません");
}catch(e){
  console.log("エラーが発生しました!!!!!");
  console.log(e);
}

このサンプルではthrow文がない代わりに、console.logで変数aを表示しようとしています。この場合、変数aは存在しませんのでエラーとなります。先に説明したようにthrow文以外の要因でエラーが投げられることがあり、これもその一例です。このような場合でもtry-catch文で扱うことができます。

今回発生したエラーを表示してみると「ReferenceError」というようなことが書いてあると思います。これはReferenceErrorのインスタンスであり、また、Errorを継承しています。instanceofを使って確かめてみましょう。


try{
  console.log(a);
}catch(e){
  console.log(e instanceof ReferenceError);
  console.log(e instanceof Error);
}

さて、例外を利用したもう少し実用的な例を考えてみましょう。九章第六回のサンプルを持ってきました。


var obj = {
  _a : 0,
  get a(){return this._a; },
  set a(n){
    if(n===true || n===false){
      this._a = n;
    }
  }
};

このオブジェクトobjのプロパティaに真偽値以外は代入できなくするというサンプルでした。これを、真偽値以外を代入しようとするとエラーが発生するようにしてみましょう。それは次のようにすればいいですね。


var obj = {
  _a : 0,
  get a(){return this._a; },
  set a(n){
    if(n===true || n===false){
      this._a = n;
    }else{
      throw new Error("その値は代入できません!");
    }
  }
};

こうすることで、間違って変な値を代入したときにエラーが発生しプログラムが止まるので、バグに気付きやすくなります。

また、例えばユーザーが入力した値がおかしかったらエラーをユーザーに通知したい場合はtry-catch文が利用できます。プロパティへの代入でエラーが発生したらそれをcatchして何らかの方法でユーザーに通知すればいいのです。

finally

さて、実はtry-catch文には少し異なる形があります。それがtry-catch-finally文です。これは、catchブロックの次に第3のブロックとしてfinallyブロックがあるというものです。finallyエラーが起きた場合でも起きなかった場合でも最終的に実行される部分となります。つまり、以下の文において処理1でエラーが起きなかった場合は処理1→処理3となり、処理1でエラーが起きた場合は処理1(中断)→処理2→処理3となります。


try{
  処理1
}
catch(e){
  処理2
}
finally{
  処理3
}

また、上の例からcatchを省略したtry-finally文もあります。これは、処理2で何もしないのと同様です。