NACの勉強の経過を綴るところ

個人的なコードの勉強

【JS】try~catch~finally命令による例外処理

JS逆引きレシピ本、044(P80)。


知ってはいたけれど、曖昧なまま放置してきたのがこの「try~catch~finally命令」だ。
ここで改めて整理して、きちんと理解できるようになっておきたい。


try~catch~finally命令は、ざっくりと言うと、以下のように書く構文である。

try {
    // 基本の命令群
} catch(hoge) {
    // 例外が発生した場合に実行する命令群
} finally {
    // 例外の有無に関わらず実行される命令群
}


例えばエラーが発生する可能性がありうる命令群がある場合、普通に命令群を書いておくと、エラーが発生した場合に、その後の挙動をコントロール出来ない。
このtry~catch~finally命令を記述しておけば、エラーが発生しても、そのエラーを「例外」として処理に組み込んでおき「エラーが発生したらこういう動きをする」と決めておくことができる。そのため、処理全体で想定外のエラーなどに振り回される可能性が減る、というわけだ。

throw命令

この「例外」を、システムエラーに限定せず、自分でコントロールするのが「throw命令」である。
「throw命令」は、システムが用意しているデフォルトのエラーメッセージではなく、自分でエラーの内容とメッセージを指定できる。


本によると、主なエラーオブジェクトは以下の通り。

オブジェクト エラー
Error 一般的なエラー
EvalError eval関数に関連するエラー
RangeError 値が許容範囲外、値が配列内に存在しないエラー
ReferenceError 未宣言の変数が参照されたエラー
SyntaxError 文法エラー
TypeError 値が期待した型でない場合のエラー
URIError 不正なURIである場合のエラー


throw命令の例としては、例えば以下のスクリプトは、半径を渡すと円の面積を返す関数である。

// 関数
function getArea(radius) {
    if (typeof(radius) !== 'number' || radius <= 0) {
        throw new TypError('引数radiusuは、正の数値である必要があります');
    }
    return tadius * radius * Math.PI;
}

// 関数に負の値を渡している
try {
    colsple.log(getArea(-3));
} catch(ex) {
    console.log(ex.message);
}
// 結果:引数radiusuは、正の数値である必要があります

// 正しい値を渡している
try {
    colsple.log(getArea(3));
} catch(ex) {
    console.log(ex.message);
}
// 結果:28.2743

try~catch命令を使用すれば、不正な値が渡された場合にエラーメッセージを管理できる。
結局、どういう際に例外を返すか、という条件はif文で書いているので、わざわざthrow命令で書く必要あるか? と一瞬思ったのだが、この場合、返り値は面積であり、エラーメッセージではない。「例外」としてエラーメッセージを投げて、try~catchで処理すれば、流れとしてはエレガントである。


ただ、正直に言えば、まだ完全に理解できていない部分があるのは確か。
特に気になるのは、上記で表にまとめたエラーオブジェクトの部分。

もちろん、適切なXxxxxErrorがない場合、Error(XxxxxError)を継承して新たなErrorオブジェクトを定義してもかまいません(たとえばファイルに関連するエラーであれば、FileErrorのようなオブジェクトを定義することになるでしょう)

サンプルスクリプトをみると、throw命令でTypeErrorをnewで書いているので、ここは実際には何というエラーオブジェクトでもいい、ということだと思う。
だとしたら、上記の表のエラーも、別に「こういうエラーの場合はこのエラーオブジェクト名」と決まっているわけではなくて、単に「分かりやすいからこういうふうに表記しているだけ」ということ?


実際、サンプルスクリプトを見る限り、throwするか否かはif文で条件を書いているわけだから、単に、こちらで決めた条件に従って例外を書いているだけ、という気がする。


一つの関数で複数の例外を投げる可能性がある場合には、それぞれユニークなエラーオブジェクトを書いて、catchでそれぞれ別の処理をさせることは可能なようだ。オブジェクト名はそういう場合だけ使い分ければいいのかな。
まぁ、確かに、エラーの内容に応じたエラーオブジェクト名のほうが、可読性は高くなる気はするけれど。
このあたり、「なんのためにそうしているのか」という、裏にある意味を理解しないと、結局そのままスルーして半可通になってしまう気がするんだよなー……。