throwの概要
| 例外のスロー JavaScript予約語 | ||
|
throw 概要 わかりやすく説明
|
||
|
throw文の基本的な使い方
JavaScriptが自動で起こすバグのエラーとは異なり、throw は「ユーザーがパスワードを入力し忘れた」「割り算の分母に0が渡された」など、プログラム的には動くけれど業務ルール上「これ以上進めてはダメな異常事態」を検知したときに自発的に使用します。
以下は、不正な計算を検知して throw で処理を中断させ、それを安全にキャッチする基本的な例です。
// 割り算を行う関数
function divide(a, b) {
// 分母が0だった場合、数学的に不可能なため処理を中断させたい
if (b === 0) {
// throw文で新しいエラーオブジェクトを作成して投げます
throw new Error("ゼロで割ることはできません。");
}
// エラーが起きた場合は上の行で処理が飛ぶため、この下の行は実行されません
return a / b;
}
try {
console.log(divide(10, 2)); // 出力: 5(正常な処理)
console.log(divide(10, 0)); // ❗ ここでエラーが発生し、即座にcatchブロックへワープします
console.log("この行は実行されません");
} catch (error) {
// throwされたErrorオブジェクトが引数(error)に格納されます
console.error("例外をキャッチしました:", error.message); // 出力: ゼロで割ることはできません。
}
if文などの条件分岐とthrowを組み合わせることで、想定外の不正なデータがシステムの奥深くに入り込むのを水際でガードできます。tryブロックの内部でthrowが実行された瞬間に、それ以降のコードはすべて無視され、即座にcatchブロックの安全なエラーハンドリングへ処理が移ります。
throw文の応用例
実際のWebアプリケーション開発では、単なる Error オブジェクトを投げるだけでなく、独自のカスタムエラークラス(例:入力チェック専用のエラーなど)を作って throw することもあります。
これにより、catch 側で「何が原因のエラーなのか」を instanceof を使って正確に見極め、表示する警告メッセージをきれいに分岐できるようになります。
// 入力チェック(バリデーション)専用のカスタムエラークラスを作成
class ValidationError extends Error {
constructor(message) {
super(message); // 親クラス(Error)のコンストラクタを呼び出します
this.name = "ValidationError"; // エラーの名前を上書き
}
}
function validateUser(user) {
if (!user.name) {
throw new ValidationError("名前が入力されていません。");
}
if (user.age < 18) {
throw new ValidationError("18歳未満のユーザーは登録できません。");
}
console.log("ユーザー検証OK:", user.name);
}
try {
validateUser({ name: "John", age: 17 }); // ❗ 年齢制限でthrowされます
} catch (error) {
// 発生したエラーが「ValidationError」の仲間かどうかを判定します
if (error instanceof ValidationError) {
console.error("【入力チェックエラー】", error.message);
} else {
console.error("【予期せぬシステムエラー】", error);
}
}
- 標準のエラーと自作のエラーを明確に区別することで、「ユーザーのミスだから画面に優しい案内を出す」「システムの深刻なバグだから管理者に通報する」といった柔軟な設計が可能になります。
instanceofを用いた種類別のエラーハンドリングと組み合わせることで、堅牢なアプリケーションの基盤を構築できます。
注意点
- 適切なエラーオブジェクトの使用:
throw "エラー発生"のように、ただの文字列や数値をそのまま投げることも文法的には可能です。
しかし、文字列のままだとエラーがコードの何行目で発生したのかという追跡情報(スタックトレース)が一切付与されないため、原因究明が非常に困難になります。必ずnew Error()を使用して投げましょう。// ❌ NGな例:ただの文字列をスローすると、デバッグが難しくなります throw "入力エラー"; // ⭕ OKな例:Errorオブジェクトを生成して投げるのが世界の共通ルールです throw new Error("入力エラーが発生しました"); - 例外処理の範囲: 自分が作成した関数の中でどれだけ激しく
throwを実行しても、それを呼び出す外側のコードのどこにもtry...catchの防護壁が用意されていない場合、ブラウザの画面が固まったり、アプリ全体がクラッシュして停止してしまいます。 - 過剰な例外処理を避ける:
throwは「通常のプログラムの実行を強制終了させてワープする」という、非常に強力でコンピュータに負荷のかかる特殊な処理です。
「ボタンのオン・オフ」や「単なる数値の大小比較」といった、通常のif / else文の分岐で簡単に制御できるような一般的な処理の流れの中で、むやみやたらにthrowを乱発するのはコードの可読性を下げるため避けましょう。
よくある質問
- Q:
throw文でスローできるデータには具体的にどのような制限がありますか? - A: 技術的な制限は一切ありません。
文字列、数値、真偽値(true/false)、普通のオブジェクト、配列、さらには関数さえもthrowで投げることが可能です。
しかし、前述の通りJavaScriptの開発現場においては、特別な理由がない限りはErrorオブジェクト、もしくはそれを継承して作ったカスタムエラーオブジェクトを投げるのが鉄則となっています。 - Q:
throwとreturnの決定的な違いは何ですか? - A: どちらも「その場所で処理を終わらせる」という点では似ていますが、目的と終わらせ方が全く異なります。
returnは、関数が予定通りの仕事をやり遂げて、呼び出し元に「はい、これが結果のデータだよ」ときれいにバトンを返す正常終了のためのコマンドです。
一方でthrowは、「これ以上仕事は続けられない!」と仕事を放棄して、エラーを解決できる場所まで強制的にワープする異常終了(緊急事態)のためのコマンドです。// return と throw のイメージの違い function checkValue(val) { if (val === "OK") return "手続き完了"; // ⭕ 正常に結果を納品 throw new Error("手続き不備"); // 🛑 異常事態として強制ストップ } - Q: キャッチしたエラーを、ログだけ記録してさらに上の処理へそのまま横流しすることはできますか?
- A: はい、可能です。
catchブロックの中で受け取ったエラーオブジェクトを、もう一度throw error;と記述することで、一度捕まえたエラーをさらに外側のtry...catchへ向けて「再スロー(再投げ)」することができます。
「この関数内ではエラーログの保存だけを行い、実際の画面へのエラー表示は親玉の関数に任せたい」というような役割分担の際によく使われます。// エラーの再スローの例 try { try { throw new Error("通信失敗"); } catch (innerError) { console.warn("(子)ログに一時記録しました"); throw innerError; // ❗ 捕まえたエラーをそのまま外へ投げ直します } } catch (outerError) { console.error("(親)最終的にユーザーへエラーを表示します:", outerError.message); }
まとめ
throw文は、予期しない不正な操作や致命的なデータ崩壊からアプリケーションを守るために、自ら警告のホイッスルを鳴らす強力な構文です。
try...catchのセーフティネットと正しく組み合わせることで、予期せぬエラーが起きてもアプリをクラッシュさせずに安全に復旧させることができます。- 文字列をそのまま投げるのではなく、デバッグに必要な情報がぎっしり詰まった
Errorオブジェクト(またはそのカスタムクラス)を投げるのが基本の綺麗なルールです。 returnによる正常な処理の終了と、throwによる緊急事態の処理中断の性質を正しく理解し、システムの信頼性と安全性を最大化させていきましょう。