JavaScriptで電卓アプリを作ってみよう(総合演習)
ここまでの JavaScript レッスンで学んだことを全部つないで、動く電卓を1つ完成させます。新しい構文は出てきません。関数・switch文・try...catch・split・配列——すでに練習したものだけで作れます。
作るのは「計算エンジン」としての電卓です。"12 + 5"のような式を渡すと答えが返り、計算履歴が残ります。画面にボタンを並べる部分はDOM操作といって、このカテゴリでは扱っていないので入れていません(下の「よくある質問」で進み方を案内しています)。
1ステップずつ進むので、途中で止めてもそこまでの分は動きます。各ステップの「実行する」ボタンで、コードを書き換えて試すこともできます。
完成イメージ
最後まで進むと、こう表示されるところまで作ります。下の実行結果は、このページを作るときに実際にコードを走らせて得たものです。
12 + 5 = 17 100 - 38 = 62 7 * 6 = 42 45 / 4 = 11.25 0.1 + 0.2 = 0.3 10 / 0 → エラー: 0では割れません 3 ^ 2 → エラー: 知らない演算子です: ^ --- 計算履歴 --- 1. 12 + 5 = 17 2. 100 - 38 = 62 3. 7 * 6 = 42 4. 45 / 4 = 11.25 5. 0.1 + 0.2 = 0.3 計算できたのは 5 件です。
STEP 1四則演算を関数にする
🎯 このステップのゴール: 足す・引く・掛ける・割るを、それぞれ関数として切り出す
まずは電卓の心臓部、四則演算から作ります。a + bと直接書いてもいいのですが、あえて関数に切り出します。
理由は次のステップで分かります。「+ならこの関数、-ならこの関数」と後から選べるようにするためです。処理に名前が付いていると、選んで呼び分けられます。
関数はfunction add(a, b) { return a + b; }という形です。returnを書き忘れるとundefinedが返るので気をつけてください。エラーにならず、計算結果だけが消えるので原因を見つけにくい間違いです。
17 62 42 11.25
STEP 2演算子で処理を振り分ける
🎯 このステップのゴール: switch文で、記号に応じた関数を呼び分ける
電卓は「どの計算をするか」を記号で受け取ります。calc(12, "+", 5)と呼べるようにしましょう。
ここで活きるのがswitch文です。ifを4つ並べても動きますが、「1つの値を見て行き先を選ぶ」ときはswitchのほうが意図がはっきりします。
注意点が2つあります。ひとつはbreakで、書き忘れると次のcaseに流れ込みます(フォールスルー)。ただしこのコードのようにreturnで関数を抜ける場合はbreakが要りません——returnした時点で関数ごと終わるからです。
もうひとつがdefaultです。知らない記号が来たときにここへ来ます。今は文字列を返していますが、次のステップでちゃんとしたエラーにします。
17 42 知らない演算子です
STEP 30で割る事故を防ぐ
🎯 このステップのゴール: throw でエラーを起こし、try...catch で受け止める
電卓として困るのが0で割ったときです。JavaScriptでは10 / 0がエラーにならず、Infinityという値を返します。計算が止まらないまま、おかしな値が先へ流れていきます。
そこで自分でエラーを起こします。throw new Error("0では割れません")と書くと、その場で処理が中断して呼び出し元へ飛びます。
受け止めるのがtry...catchです。tryの中で起きたエラーはcatchに渡され、プログラム全体が止まらずに済みます。err.messageで理由の文字列を取り出せます。
「エラーを自分で起こす」のは一見おかしな感じがしますが、間違った値のまま計算を続けるほうがずっと危険です。早めに止めて理由を伝えるのが、壊れにくいプログラムの作り方です。
12 + 5 = 17 10 / 0 → エラー: 0では割れません 3 ^ 2 → エラー: 知らない演算子です: ^
STEP 4文字で書いた式を読み取る
🎯 このステップのゴール: split で式を分解し、Number で数値に変換する
いよいよ電卓らしくします。calc(12, "+", 5)ではなく、"12 + 5"という1本の文字列を渡せるようにしましょう。
使うのはsplitです。expression.trim().split(/\s+/)と書くと、前後の空白を落としてから、空白のかたまりで区切ります。正規表現の\s+を使うのは、空白が2つ以上あってもうまく分けるためです。
ここで大事なのが型変換です。splitが返すのは文字列の配列なので、"12"のままだと"12" + "5"が"125"になってしまいます。Number(parts[0])で数値に直してから計算します。
変換に失敗するとNaN(非数)になります。NaN === NaNはfalseという有名な性質があるので、判定にはNumber.isNaN()を使います。
12 + 5 = 17 100 - 38 = 62 あ + 5 → エラー: 数値として読めません: あ + 5 12 + → エラー: 「数値 演算子 数値」の形で書いてください: 12 +
STEP 5小数の誤差をなくす
🎯 このステップのゴール: 0.1 + 0.2 が 0.3 にならない理由を知り、丸めて直す
ここまでの電卓で"0.1 + 0.2"を計算してみてください。答えは0.30000000000000004になります。電卓としては困ります。
これは不具合ではありません。JavaScriptの数値は64ビットの浮動小数点で、0.1も0.2も2進数では正確に表せないためです。無限に続く小数をどこかで切っているので、足すとわずかな差が残ります。同じことはPythonでもJavaでも起こります。
対策は丸めることです。Math.round(value * 1e10) / 1e10と書くと、いったん10桁ぶん大きくして整数に丸め、また戻します。誤差は小数第16位あたりに出るので、第10位で丸めれば消えます。実際に使う桁数は問題ありません。
お金の計算では、この方法でも足りません。1円単位で正確に扱いたいときは、はじめから「円」ではなく「銭」のような整数で持つのが定石です。誤差の出ない世界に持ち込んでしまう、という考え方です。
丸める前: 0.30000000000000004 丸めた後: 0.3 等しいか: false 丸めれば: true
STEP 6計算履歴を残して仕上げる
🎯 このステップのゴール: 配列に履歴を積み、まとめて表示して完成させる
最後の仕上げです。計算するたびに履歴を配列へ積んで、最後にまとめて表示します。これで電卓の完成です。
履歴はconst history = []で用意し、history.push(...)で足していきます。constなのに中身を足せるのを不思議に思うかもしれません。constが禁じているのは「その変数に別のものを入れ直すこと」で、配列の中身を変えるのは止めません。
表示にはforEachを使い、${i + 1}で1から始まる番号を付けています。配列の添字は0から始まるので、人が読む番号にするには1を足します。
エラーになった式は履歴に入りません。history.pushがevaluateの中、しかもthrowより後にあるからです。途中でthrowすると、それより下は実行されません——この性質をそのまま使っています。
完成です。run("...")の式を書き換えて、いろいろ計算させてみてください。
12 + 5 = 17 100 - 38 = 62 7 * 6 = 42 45 / 4 = 11.25 0.1 + 0.2 = 0.3 10 / 0 → エラー: 0では割れません 3 ^ 2 → エラー: 知らない演算子です: ^ --- 計算履歴 --- 1. 12 + 5 = 17 2. 100 - 38 = 62 3. 7 * 6 = 42 4. 45 / 4 = 11.25 5. 0.1 + 0.2 = 0.3 計算できたのは 5 件です。
よくある質問
画面にボタンを並べた電卓にするには?
ボタンを押して動く電卓にするにはDOM操作(document.querySelectorやaddEventListener)が必要ですが、これはJavaScriptカテゴリでは扱っていません。無理に入れると未習の内容だらけになるので、このページでは計算エンジンまでを作っています。
見た目を作るところはHTML/CSSカテゴリが近道です。このページで作ったevaluate()はそのまま使えます。ボタンからevaluate()を呼ぶだけで画面付きの電卓になります。
エラーが出て動きません
多いのはreturnの書き忘れです。関数がundefinedを返しますがエラーにはならないので、計算結果だけが消えます。
次に多いのがバッククォートの打ち間違いです。${...}を使うテンプレートリテラルは、囲む記号がシングルクォートではなくバッククォートです。ここを間違えると${a}がそのまま文字として出ます。
かっこ付きの式も計算したい
"(1 + 2) * 3"のような式を扱うには、優先順位を解釈する仕組みが要ります。その第一歩が括弧の対応をチェックしようで学ぶスタックです。まずはこのページの形を動かしてから進むのがおすすめです。
🚩 次はこのカテゴリへ
電卓の中身ができたら、次は見た目です。HTML/CSSカテゴリでは、ここで作った計算エンジンに画面を付けるための土台——ボタンやレイアウトの作り方を学べます。
HTML/CSSのレッスンへ進む