プログラミングをやっているとエラーは日常的に対応する作業です。
初学者の方からすると「エラー=怖いもの」と言うイメージがある方が多いのではないでしょうか?
確かに偶発的に発生するのもエラーの代表ですが、プログラミングの世界では「わざとエラーにする」なんてことも日常的に使います。
例えば不正な操作があったときに通常稼働せずにエラーにしてシステムを停止させた方が運営上は適切だからです。
そんな感じでエラーは私たちエンジニアが自分達で「主体的に」作るものであり、今回はJavaScriptを使ってエラーの作り方を解説していきたいと思います。
動画もあるので必要に応じて使ってください。
JavaScriptにおけるエラーの作成方法の基本
まず基本的なエラーの作成方法から紹介します。
エラーと聞くと「偶然起こるもの」と思われるかもしれませんが、何もないところから自分達で作ることもできるんです。
例えば下記のように引数に入れる数字によって正常とエラーを分ける関数を作ってみます。
引数に入れる数字が20以下であればコンソールに数字を表示して、21以上であればエラーということにしてエラーであることをコンソールに表示します。
function func(val) {
if (val > 20) {
const error = new Error("エラーです");
throw error;
} else {
console.log(val);
}
}
func(10);
引数に10を入れたので条件分岐のelseである正常パターンを通って、コンソールに引数の10がそのまま表示されましたね。
続いてはエラーになるように30を引数に入れてみます。
function func(val) {
if (val > 20) {
const error = new Error("エラーです");
throw error;
} else {
console.log(val);
}
}
// ここを修正
func(30);
こんな感じでエラー文をコンソールに表示することができました。
JavaScriptではnew Error( )とするとエラーを作成することができて、引数にエラーの文章を自分で指定することができます。
new Error( )でエラーを作った時は定数などに格納してthrowすることがルールになっています。
上記では定数errorに作成したエラーを格納したので、throw errorとしていますね。
初学者の方だとエラーを自分で作成したり、エラーの文章を考えることは少ないかもしれません。
エラーと一言に言っても今回のようなコンソールに表示するパターン以外にも色んなパターンがあります。
どんな方法だとしてもエラーを作成する目的は「プログラムが止まったことを知る」ことには変わりません。
ちなみにですが今回の関数でエラーの文章を作ってなかった場合はどうなるのでしょうか?
function func(val) {
// ここを修正
if (val < 21) {
console.log(val);
}
}
func(30);
引数が20以下であればコンソールに表示する、というシンプルな条件分岐になりました。
一見するとコードが短くなって良さそうに見えますよね。
しかし同じくエラーになる引数を入れた場合にコンソールに何も表示されていません。
こちらプログラムとしては止まっていますが、パッと見た感じでは成功したのか失敗したのか分からない状態です。
アプリをリリースして何かの異常でシステムが止まっているのに、エンジニアもユーザーも誰も気づかず放置していたとしたら恐ろしいはずです。
「プログラムが止まったことを知らせる」という意味でエラーは大事な現象だということを実感してもらえたでしょうか?
JavaScriptにおけるtry catchの使い方
エラーを自分達で作り出すということを体験してもらいました。
JavaScriptではエラー処理も含めて便利な条件分岐の文法が用意されていて、try catchという以下のような書き方ができます。
const num = 100;
function tryFunc() {
try {
console.log(num);
} catch (error) {
console.error(error);
}
}
tryFunc();
関数tryFunc( )の中でtry catchを使っています。
tryには実行した処理を、catchでは実行したい処理が失敗したときのエラー処理を書きます。
今回のtryの内容は定数numの数字をコンソールに表示するものですので正常に動作しています。
それではtryの中を失敗するように書き換えてみましょう。
const num = 100;
function tryFunc() {
try {
// ここを修正
num = num + 1;
} catch (error) {
console.error(error);
}
}
tryFunc();
tryの中の処理をnumに1を加算するように修正してみました。
numは定数なので後から計算することはできませんので失敗して、catchの中に移動してコンソールにエラーを表示した形です。
書き方こそ違いますがif文と似たような考え方ですね。
とはいえif文とは明確に違う点がありますので紹介します。
例えば上記のコードでtry catchの後に別の処理があったとします。
const num = 100;
function tryFunc() {
try {
num = num + 1;
} catch (error) {
console.error(error);
}
// ここを追加
console.log("関数のコンソールです");
}
tryFunc();
try catchが終わった後に別のコンソールログの処理を追加しました。
tryの中身については引き続き失敗する内容になっています。
この状態でコンソールには何が表示されるでしょうか?
最初のエラーについては先ほどと同じものなのですが、2行目に後続のコンソールログが表示されていますね。
同じようなものを冒頭でやったif文でも試してみましょう。
function func(val) {
if (val > 20) {
const error = new Error("エラーです");
throw error;
} else {
console.log(val);
}
// ここを追加
console.log("関数のコンソールです");
}
func(30);
こちらもif文が終わった後に、別のコンソールログの処理を追加しました。
エラー時の動作を見たいので関数の引数にエラーになる30を入れて実行します。
もちろんエラーになるわけですが、後続に書いていたコンソールログは表示されませんでした。
これがif文とtry catchの大きな違いになります。
try catchはエラーになったとしても後続の処理は実行するような仕様です。
どちらが良いか悪いかというより、自分が作ろうとしている処理によって使い分けるようにしましょう。
とはいえtry catchでもエラーになったときに後続を実行させない方法があります。
const num = 100;
function tryFunc() {
try {
num = num + 1;
} catch (error) {
console.error(error);
// ここを追加
return;
}
console.log("関数のコンソールです");
}
tryFunc();
上記のコードのようにエラー処理を書くcatchの中で最後に戻り値を設定すると、try catchの後続にある処理が実行されなくなります。
catchの中で設定する戻り値は通常の関数と同じように考えれば大丈夫です。
今回は特に戻すものはないのでreturnとだけ書きました。
try catchのfinallyについて
実はtry catchにはfinallyという第三のエリアを書くことができるので紹介します。
const num = 100;
function tryFunc() {
try {
num = num + 1;
} catch (error) {
console.error(error);
// ここから修正
} finally {
console.log("ファイナリーのコンソールです");
}
console.log("関数のコンソールです");
}
tryFunc();
finallyのエリアには新しくコンソールログを表示する処理を書いてみました。
また先ほど試したcatchの中にあるreturnは削除しています。
この状態でコンソールに何が表示されるか確認してみます。
tryの中身が引き続き失敗するので最初にエラーが表示されます。
続いてfinallyの中に書いたコンソールログが表示され、最後にtry catchの後に書いたコンソールログが表示されました。
finallyの中ではtryの中で起きた成功、失敗に関わらず実行したいものを書くことになっています。
システムの一部でエラーが起きたからと言って全ての機能を停止させることばかりではないからです。
ここまで読んできた方の中で「try catchの後続に書いておけば良いのでは?」と思われた方がいるかもしれません。
こちら先ほど触れたcatch内の戻り値の設定によって違いが出ます。
catchの最後にもう一度returnを追加して実行します。
const num = 100;
function tryFunc() {
try {
num = num + 1;
} catch (error) {
console.error(error);
// ここを追加
return;
} finally {
console.log("ファイナリーのコンソールです");
}
console.log("関数のコンソールです");
}
tryFunc();
catchの最後に戻り値を設定したにも関わらず、finallyの中のコンソールログが実行されていますね。
これがfinallyの特徴です、catchに戻り値がある状態でエラーになると後続の処理も止まるのですがfinallyの中に書いたものだけは実行されるのです。
catchに戻り値を設定しなければfinallyをわざわざ書く必要はないですが、正常でも失敗でも実行したいものはfinallyとして書くようにする人が多い印象です。
今の状態ではcatchに戻り値がなくても、何かの追加や修正によってcatchに戻り値が後から追加されることがあるからです。
プログラミングは作って終わりということは少なく、リリースしてからも修正や追加があることが多いです。
後からの変更に出来るだけ影響されづらい書き方を意識できるように練習してみましょう。