JavaScriptの例外とthrow文の考え方
プログラムは「うまくいく道筋」だけでなく、「うまくいかなかったときの道筋」も用意しておくと、安心して動かせます。例外は「ここで止めたい」「この条件はダメ」と知らせる仕組みです。throwはその「知らせ」を自分で出す合図、try...catchはその「知らせ」を受け止めて安全に処理を続けるための枠組みです。
例外の基本とthrow文
- 例外とは:
「通常の流れを中断して、特別な処理へジャンプする」仕組み。バグだけでなく、想定内の「ダメな状態」(未入力、範囲外、接続失敗など)も例外にできます。 - throwの役割:
その場で例外を発生させ、以降の処理を止めて、いちばん近いcatchへ渡します。 - 基本の書き方:
throw 値; // 文字列でもOKだが…
throw new Error('メッセージ'); // これが定番(おすすめ)
JavaScript文字列よりErrorオブジェクトを使うと、エラーメッセージや種類、スタック情報が扱いやすくなります。
try…catchとfinallyの使い方
- 構造:
try {
// 普通の処理(ここでthrowが起きるかもしれない)
} catch (e) {
// 例外を受け止める(eに例外情報が入る)
} finally {
// 成功でも失敗でも最後に必ず実行(後片付け)
}
JavaScript- ポイント:
- try: 失敗するかもしれない「危ない処理」を入れる場所。
- catch: 失敗したときの「代わりの動き」。ユーザーへの案内、ログ記録など。
- finally: ファイルを閉じる、ローディング表示を消すなどの後片付け。例外の有無に関わらず実行されます。
例題で学ぶ(初心者向け)
入力チェック:空文字は受け付けない
function greet(name) {
try {
if (!name || name.trim() === '') {
throw new Error('名前を入力してください'); // ダメな状態を明確化
}
console.log(`こんにちは、${name}さん!`);
} catch (e) {
console.error('エラー:', e.message); // ユーザー向けのわかりやすい文
} finally {
console.log('greet()の処理を終了しました'); // 後片付けやログ
}
}
greet('太郎'); // → こんにちは、太郎さん!
greet(' '); // → エラー: 名前を入力してください
JavaScript- 狙い:
空入力を例外にすることで、誤った状態を早めに止め、理由をはっきり伝えます。
範囲チェック:点数は0〜100だけ有効
function setScore(score) {
try {
if (typeof score !== 'number') {
throw new TypeError('scoreは数値である必要があります');
}
if (score < 0 || score > 100) {
throw new RangeError('scoreは0〜100の範囲で指定してください');
}
console.log('保存しました:', score);
} catch (e) {
console.error(`${e.name}: ${e.message}`); // 例外の種類も表示
}
}
setScore(85); // → 保存しました: 85
setScore('85'); // → TypeError: scoreは数値である必要があります
setScore(120); // → RangeError: scoreは0〜100の範囲で指定してください
JavaScript- 狙い:
型と範囲の2段階チェック。Errorのサブタイプで「何がダメか」をより明確にします。
疑似API呼び出し:失敗したら安全に復帰
function fetchUser(id) {
try {
if (typeof id !== 'number' || id <= 0) {
throw new Error('ユーザーIDが不正です');
}
// 疑似的に失敗することがある処理
const success = Math.random() > 0.5;
if (!success) {
throw new Error('サーバーに接続できませんでした');
}
console.log('ユーザー取得成功:', { id, name: 'Hanako' });
} catch (e) {
console.warn('取得に失敗:', e.message);
console.log('代替処理: ゲストユーザーを表示します');
} finally {
console.log('読み込み表示を終了'); // スピナーやローダーを消す
}
}
fetchUser(1); // 成功/失敗がランダム
fetchUser(-1); // → ユーザーIDが不正です
JavaScript- 狙い:
外部要因で失敗しやすい処理は、失敗時の代替ルート(メッセージ表示・仮データ)を用意するとUXが安定。
実務で使えるコツ
- 明確なメッセージ:
何が原因で、何をすれば良いかが伝わる文章にする(例:「メール形式が不正。example@domainの形式で入力してください」)。 - 早期に投げる:
ダメな状態を見つけたら、その場でthrowして後続処理を実行させない。バグの発見が早まり、データ破壊を防げます。 - 特定のError種類を使う:
TypeError(型が違う)、RangeError(範囲外)、SyntaxError(文法)など、種類で原因がわかりやすくなる。 - ユーザー向けと開発者向けを分ける:
ユーザーにはやさしいメッセージ、ログには詳細情報(e.stackなど)を残す。 - finallyで後片付け:
ローディング解除、ファイル・接続のクローズなどはfinallyに置くと抜け漏れしません。
よくあるつまずき
- throwしたのにcatchされない:
同じ同期の呼び出しチェーン内にtry...catchが必要。非同期(setTimeoutやPromise)では、別の扱いになるので注意。 - 文字列をthrowして解析しづらい:
throw 'エラー'より、throw new Error('エラー')を推奨。nameやstackが使えるため、原因調査が楽です。 - 例外を握りつぶす:
catchで何もせず無視すると、問題が隠れて品質が落ちます。最低限ログを残すか、ユーザーに知らせる対応を。
