BETA

初めてのJavaScript【電卓の作成】

投稿日:2019-04-02
最終更新:2019-04-02

この記事は2019.03/27に書きました。

はじめに。

初めまして、Twitterで@okita_kamegoroというアカウントで呟いているOkitaです。私はプログラミングとは無縁な大学院生なのですが、最近興味を持って独学で勉強しています。
「自分でサービスを作れるようになれたらな〜〜」と思い立ち、現在はHTML, CSS, JavaScriptを勉強しているとこです。
先日、2日間かけてProgateのJavascriptコースを1周したので、手始めに電卓を自作してみました。作成にあたって学んだことや気付いた事を備忘録を兼ねて書きたいと思います。
ソースコードも公開するので、「こんな書き方もあるよ」「ここは直した方がいいかも」などアドバイスをいただけたら嬉しいです!!
以下がソースコードへのリンクです。
JsCalculator on Github

前提条件

電卓を作るにあたっては、iPhone(iOS)にデフォルトで入っている電卓を模倣することにしました。
計算するときの挙動もできるだけ似せようと思います。

そして作るにあたっては2つの制約を課すことにしました。

  • ネットで電卓のソースコードをみないようにする。
  • あとでコードを見返した時に直ぐに分かるくらいシンプルに書く

です。
1つ目は初めから答えをみたら面白くないからです。
2つめは大事で、コードをシンプルに記述する力はプログラミングの際に重要な気がしたので、訓練の為に心がけていきたいものです。
またコードを書く上では、「機能を出来るだけ分割して小分けにして書く」「条件分岐(if, for)などは極力書かない」ということを意識して書くようにしようと思います。
github上でオープンソースのコードを眺めていると、やたら短いfunctionやメソッドが沢山定義されている様子を見かけることがあります。この理由はよくわからないのですが、小分けにすることでコードを見やすくしているような気がします。
したがって2つ目の制約に当てはまるように少し真似して書いて見たいと思います。

作成の流れ

作成の流れは以下のようになっています。この記事でもこの順番に書こうと思います。

  • 電卓のデザイン・仕様の確認
  • 電卓のデザイン(HTML5, CSS3)
  • 電卓の機能の実装 (JavaScriot ES6)
    そして最後に作成した際に当たった壁とその解決法を述べて終わりにします。

電卓のデザイン・仕様の確認

電卓のデザインを確認

iPhoneの電卓のスクリーンショットです。


ここで、入力用のボタンを入力ボタン、数字が表示される場所を表示画面と呼ぶことにします。
当然ですが各入力ボタンが綺麗に配列しています。そこでボタンの配置にはHTMLのTableタグを使いたいと思います。
また、入力用のボタンが3色に色分けされており何か意味がありそうです。
なので今回の電卓作成ではCSSのクラスを以下のように3分割して作ろうと思います。

  • 四則演算を実行する演算子 +, -, ×, ÷, =
  • テンキー 0-9,とピリオド
  • その他の機能 AC, 符号反転, パーセント計算

また、0のボタンが他のボタン2つ分になっています。これもセルを結合することで再現します。

電卓の機能・仕様

電卓の仕様を確認します。

§ 入力から数値の確定

電卓では127という値を入力するのに1, 2, 7と順番に入力し、+ボタンなどの演算子のボタンを入力することで値が確定します。したがって、どのように値の入力を判定するかが問題です。
やり方としては2つ考えました。

  • 入力された値を変数の後ろに代入して、+, - , ×, ÷ボタンで値を確定する
  • 入力された値を表示画面の後ろに出力して+, - , ×, ÷ボタンで表示画面から値を取得する
    これは後者の方が自然な流れのような気がします。

§ 計算の流れ

どの電卓も同じですが、数値1演算子1数値2演算子2⇨....と延々に計算し続けることができます。
そして最後に=ボタンを押した際に現時点での計算結果を表示します。
このとき、注意すべき点は、数値1演算子1数値2が確定して初めて計算を実行できる点です。
その為、各処理においてこれらの値を保持する必要があります。
JavaScriptのガベージコレクションでは変数が使用されなくなった時点で変数が解放されるようです。入力ボタンを打ち込む関数に変数を定義してしまうと関数の終了時に値が解放されてあとで参照できなくなってしまいます。
その為、変数が解放されない方法を2つ考えました。

  • 各変数をテキストやブラウザ内に保存する方法
  • 変数が解放されないようにJavascriptを書く方法(グローバル変数化)

これは圧倒的に後者の方が簡単に実装できるので後者にすることにしました。JavaScriptのグローバル変数は、該当するページがunloadされるまではメモリ上に保持されるらしいです。
宣言をした変数はいつまで残り続けるのでしょうか? by Stackoverflow

ここに気付くまでかなり時間を浪費してしまいました。しかしながら、あまりグローバル変数は多用したくないので使用しない方法も今後模索する必要がありそうです。
この入力と計算はcalcuRun()calcuInput()関数で実現します。

§ =を2回押したときの挙動

計算結果を=ボタンを使って表示した後に、さらに=ボタンを押した際は計算結果がクリアされます。(⇦勝手にこういうものだと思って機能を実装してしまいました。あとで以下のように修正します。)

数値1演算子1数値2の後に=ボタンを2回押すと計算結果, 演算子1, 数値2を使って更に計算されることがわかりました。
その為、連続して押された場合の挙動について対応する必要があります。
これはcalcuEqual()という関数で実現します。

§ 符号反転%, ACの機能

これは単純に、値に-10.01をかけたり、全ての変数を初期化する関数を作ればいいと思います。
これは calcuNegated() , calcuPercent(), calcuInit() の関数で実現します。

§ .が2回打たれたときの挙動

iOSでは.を入力中に2回押すと後からの入力を無視するようです。
表示画面上のピリオドの数が1つ以上は処理を無視するようにすればいいと思います。
これは正規表現で判定しようと思います。

§ 演算子ボタンが押された後の挙動

演算子ボタン+, -, ×, ÷が押された後、表示画面に表示されている数字は次の入力があるまでは保持されています。
したがって、演算子ボタンが押されたタイミングではなく、演算子ボタンが押され、さらに数字の入力が行われたタイミング表示画面の値をクリアして次の入力を行わなければなりません。
これはvDisplayClea()という関数で実現します。

ざっと見た感じはこんなもんでしょうか。
ここまで分かったならレイアウトを作って実装してみたいと思います。

電卓のデザイン(HTML5, CSS3)

ファイルの構成は以下のようになっています。

  • index.html 電卓の起動ボタン
  • calcu.html 電卓のhtml
  • calcu.css 電卓のcss
  • calcu.js 電卓の機能

小窓化 (index.html 電卓の起動ボタン)

index.htmlに起動ボタンを設置して電卓を起動したいとおもいます。
より独立したアプリ感を出すために、ブラウザの小窓機能を使いたいと思います。
ブラウザに内蔵してあるwindow.open()という関数を使うことで小窓を表示させます。
それと、ボタンの見た目をマシにするためにbootstrapのbtn, btn-infoクラスを使います。
なので、headでbootstrapのcssを読み込みます。
下が起動用ボタンです。

<input class="btn btn-info" type="button" value="電卓を起動" onclick="window.open('calcu.html', null, 'top=100,left=100,width=350,height=605,toolbar=yes,menubar=yes,scrollbars=');">  

htmlのbody部分はこれだけで終わりです。
ボタンを押せば、リンクされているcalcu.htmlをロードして表示します。

電卓のデザイン(calcu.html, calcu.css)

特筆するものはありませんが、Javascript側で表示画面に値の入出力をするために、表示画面pタグidを付加します。
またcalcu.jsを呼び出すために、headでファイルのリンクを定義しておきます。

そして4×5のテーブルを作成して、その中にinputタグを入れます。

 <input class="cbtn-head" type="button" value="AC" onclick="calcuInit()">  

こんな感じです。
type="button"で汎用ボタンにすることを定義し、value="AC"はボタンに表示する値、onclick="calcuInit()"はボタンが押されたときに呼び出されるJavaScriptの関数です。

初めにレイアウトを作った時はjavascriptの関数を呼び出すことを全く考えずにinputタグを使用しなかったので後から挿入してレイアウトが崩れてしまい、作り直す二度手間をしてしまいました。
HTML, CSS, Javascriptがうまく連携する様に書くにはまだまだ訓練が必要そうです

電卓の機能の実装(JavaScript)

電卓の機能はcalcu.jsというファイルに書きました。
なんやかんやで関数が8つになりました。
入力に対する関数は以下の6つです。

  • calcuInput() 数値の入力用
  • calcuRun() 演算子の入力を受け取り計算を実行
  • calcuEqual() ボタンを押したときの処理
  • calcuNegated() 符号反転用(-1をかける)
  • calcuPercent() パーセント表示(0.01をかける)
  • calcuInit() 電卓の初期化

基本的な機能を提供するのはcalcuInput()calcuRun()です。
そのほかの関数は1~3行で書かれていて簡単な機能を書いています。
残りの2つは

  • それぞれの四則演算を実行するcalcuAny()
  • 入力確定(演算子ボタン入力)後に画面の表示をクリアする関数vDisplayClea()

です。この2つの命名が適当すぎますが気にしないでください 笑
とまあ作ったものはこれで以上になります。
ソースは以下のgithubにあげておきます。,
JsCalculator on Github

問題と解決

document.getElementById('').value

表示画面にはpタグを使っています。このタグから値を取得するのに.valueプロパティを参照していました。どうしても値を取得できずに調べたらStackoverflowに書いてあった。

document.getElementById().value return undefined in chrome
by Stack over flow

.valueプロパティはinputタグなどのフォームタグで使えますが、div, pなどでは使えなようです。
そのため.innerHTML.inerTextを使うことで解決。

まとめ

今回は特にハマることもなく作ることができました。しかしながら、電卓の機能をいかにプログラムに落とし込むのかを考えることに大部分の時間が取られました。悩みながら作っていく中で多くの気付きや発見を得ることができたので、実際に作ってみるという行為がプログラミングの学習においては非常に重要であると認識することができました。

また、作成後いろんなサイトで作られた電卓のコードを見ましたが、どれも1つの関数に条件分岐を多用して処理を記述しているようでした。
私が今回とった方針は「機能を出来るだけ分割して小分けにして書く」「条件分岐(if, for)などは極力書かない」というものでこれらとは正反対でした。
どちらの方がより分かりやすいのでしょうか。
私のやり方だと、機能を小分けにして関数を多く作ることにしました。すると1つの関数の中身はシンプルになるのですが、関数がいくつもあるので呼び出す側の関数の動作がイメージできなくなるのではないかと不安に思うところです。
一方で、1つの関数に機能を集約してしまえば、読み難くはあるものの、上から順に追って読めば分かるはずです。
実際の開発現場などではどのようにしてプログラムを作成しているのか気になるところです.

プログラムの作成当初は計算用のクラスを作って、四則演算の各処理をオーバーライドするといいのでは?などと考えていましたが、結局は関数にまとめて書いて条件分岐で処理を分けるやり方に落ち着きました。
これがいい方法なのかはわかりませんが、単純な見た目でわかりやすいので気に入っています。

function calcAny(ans, tmp, keyinp) {  
    if (keyinp == '+') { return ans * 1 + tmp * 1 }  
    if (keyinp == '-') { return ans * 1 - tmp * 1 }  
    if (keyinp == '×') { return ans * 1 * tmp * 1 }  
    if (keyinp == '÷') { return ans * 1 / tmp * 1 }  
}  

条件分岐を多く使わないようにした結果、なかなか思い通りの処理を書くことができず、結局if文は多用してしまいました。三項演算子などのようにif文を1行で書くテクニックも今回知ることができ、実際に使うことができたのはよかったと思います。
あとは今回調べていく中でJSdocコメントというものを知ることができました。
実際にjavascriptのファイルに書いて見たのですが、VScodeで関数を参照しやすくなったので今後は積極的に使って見たいと思います。 (これって用途合ってますかね?笑)

特にまとまってないですが、今回はこれで終わりにしたいと思います!
次回はメモ帳か何かを作って見たいと思います!

技術ブログをはじめよう Qrunch(クランチ)は、プログラマの技術アプトプットに特化したブログサービスです
駆け出しエンジニアからエキスパートまで全ての方々のアウトプットを歓迎しております!
or 外部アカウントで 登録 / ログイン する
クランチについてもっと詳しく

この記事が掲載されているブログ

@okita_kamegoroの技術ブログ

よく一緒に読まれる記事

0件のコメント

ブログ開設 or ログイン してコメントを送ってみよう
目次をみる
技術ブログをはじめよう Qrunch(クランチ)は、プログラマの技術アプトプットに特化したブログサービスです
or 外部アカウントではじめる
10秒で技術ブログが作れます!