ここでは、初心者から実践的に学べるプログラミングスクール「ウェブカツ!!」のJavaScript・jQuery部入門を受講している生徒の方向けに、さらに理解度を上げるための特訓をしていきます。
出題の回答は全て「JavaScript言語」を使って回答してください。
回答のJavaScript言語は「ES5」で答えてください。
(ES5がよく分からない人は「JavaScript・jQuery部入門どおりの書き方」ってことです。)
開発環境はMacとGoogleChromeブラウザを使います。
Windowsの人もGoogleChromeブラウザを使いましょう。
鬼練5:「仕事と私」どっちを取るのよ!恐怖の男編
前回の続きです。
彼女にめちゃくちゃ恐怖を感じさせましょう。
仕様書
下記要件に従ってプログラムを組んでみてください。
画面に
「彼女:仕事と私どっちを取るのよ!」という文字を表示させる
その次に
「自分:◯◯◯」を表示させる。
◯◯◯は変数(変数answerとする)にしておき、その変数の中身を表示させる。
変数answerには
「仕事に決まってるだろ!」
「仕事よりも君が大事さ。」
のどちらかの文字列をランダムで入れておく。
変数answerの中身が「仕事に決まってるだろ!」だった場合には、
「彼女:あたたたたたたたっーーー!!」と
「自分:ひでぶっ!!」という文字を表示させる
変数answerの中身が「仕事よりも君が大事さ。」だった場合には、
「自分:愛してるよ。」を100回表示させ、
「彼女:ひぃやぁぁぁーーーーーー!」と
「自分:ひでぶっ!!」という文字を表示させる
今回からコンソールに出さずに画面に出していきましょう。
jsで画面に表示させるにはいくつか方法がありますが、一番簡単な
1 |
document.write(文字列); |
を使いましょう。改行させるには
1 |
document.write('彼女:仕事と私どっちを取るのよ!<br>'); |
といったように末尾にhtmlの改行タグを入れれば改行されます。
答え
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
var CHOICES = { YOUR: '仕事よりも君が大事さ。', WORK: '仕事に決まってるだろ!' }; var answer = (Math.floor(Math.random()*2)) ? CHOICES.YOUR : CHOICES.WORK; document.write('彼女:仕事と私どっちを取るのよ!<br>'); document.write('自分:' + answer + '<br>'); if(answer === CHOICES.WORK){ document.write('彼女:あたたたたたたたっーーー!!<br>'); } else if (answer === CHOICES.YOUR) { for(var i = 0; i < 100; i++){ document.write('自分:愛してるよ。<br>'); } document.write('彼女:ひぃやぁぁぁーーーーーー!!<br>'); } document.write('自分:ひでぶっ!!'); |
前回と違うのは、13行目〜15行目までです。あとは全部前回といっしょ。
繰り返し処理(for文)
同じ「愛してるよ。」という文字列を何度も表示したい。といった時に使えるのが
「繰り返し処理」
ですね。そう、for文です。コレですね。
1 2 3 |
for(var i = 0; i < 100; i++){ document.write('自分:愛してるよ。<br>'); } |
for文の書き方は部活でもやりましたが、こうです。
1 2 3 |
for(ループ内だけで使う変数を定義して初期化; その変数がいくつまでループさせるか式を書く; ループ毎の終わりにしたい処理を書く){ //ループ毎に実行したい処理 } |
なので、今回の場合だと100回繰り返したいわけなので
1 2 |
for(var i = 0){ } |
とまず、ループの中だけで使われる(ループが全部終わったら勝手に変数は捨てられる)変数iを作り、その中にループが開始される時に入れておきたい値で初期化します。
この場合だと変数iには最初0からループが開始されますね。
ちなみにfor文の変数名はなんでもいいんですが、i(アイ)かl(エル)が慣習になっています。
次に「;(セミコロン)」で区切って、「その変数がいくつまでループさせるかの式」を書きます。
1 2 |
for(var i = 0; i < 100){ } |
この例だと「変数iが100に満たない間」という式です。
この式はループ毎の最初に比較され、その比較をした結果がtrueならループがまた開始され、falseならループ終了となります。
なので、
変数iは0から始まり、変数iが100に満たない間だけループを行う。
ということですね。最後にまたセミコロンを使って区切り「ループ毎の終わりにしたい処理」を書きます。
1 2 3 |
for(var i = 0; i < 100; i++){ document.write('自分:愛してるよ。<br>'); } |
この場合だと
1 |
i++ |
となっていますが、これは「インクリメント」といって
1 |
i = i + 1 |
の式と同じです。それの省略の書き方ですね。
なので、
変数iがループの終わり毎にプラス1されていきます。
なので、
最終的に変数0からループが開始され、変数iがループ毎にインクリメントされていき、変数iが99となるまで、その中の処理が実行される。
ということになりますね。
1 2 3 |
for(var i = 0; i < 100; i++){ document.write('自分:愛してるよ。<br>'); } |
これがfor文です。
鬼練6:無限の愛を持つ男編
今回はとても簡単なやつです。
仕様書
下記要件に従ってプログラムを組んでみてください。
画面に
「愛してるよ。」を0.1秒ごとに1回表示させる。
彼氏の愛の深さを感じますね。
ヒント
ヒントは「◯秒毎に何かの処理をする」というのをどう実現するか?ということですね。
for文のようにループに見えますが、for文では実現出来ません。(出来なくはないですが、かなりややこしいコードになります)
「◯秒毎に何かの処理をする」はjsに元からあるたった1つの「関数」で実現できます。
答え
1 2 3 4 |
function whisperLove(){ document.write('愛してるよ。'); } setInterval(whisperLove, 100); |
答えはこうなります。
改行など入れてコードを見やすくしていても、たった4行で実現できます。
STEP1:関数を作る
まず、「関数」というものを作ります。
関数は部活でやりましたね?
処理をひとまとめにできるもの。です。
他にもこっちの記事にもまとめてるので見てない人は見てください。
関数の作り方は
1 2 3 |
function 関数名(関数の中に渡したい値がある場合に受け取る箱の変数名を書く){ // 実行したい処理 } |
となります。
functionと書いてやって、関数名を適当な名前をつけます。
今回は、「愛してるよ」を囁くだけなので「whisperLove」という関数名にしました。
関数名はその中の実行する処理や、なんのため(役割)に作ったものなのかがパッと分かるような名前にしましょうね。
関数名のあとはカッコをつけます。
カッコの中には関数に渡したい値がある場合にそれを受け取るための変数を作っておきます。
今回は何もないので、ここらへんは省略します。詳しくは以降の鬼練でやります。
とりあえず「関数名の後にはカッコが必要」ってことです。そういうもんです。
(関数名の後ろやカッコの中や前後に半角スペース開けても大丈夫です。色々試してみましょう)
そして、波括弧でその中に「実行したい処理」を書きます。
今回は「愛してるよ。」を画面に表示する処理ですね。今までにやったdocument.write()を使います。
波括弧も改行してもしなくても動きますが、
1 |
function whisperLove(){document.write('愛してるよ。');} |
よりも、
1 2 3 |
function whisperLove(){ document.write('愛してるよ。'); } |
の方が断然見やすいですよね。
これで「関数」の作成は完了です。
ちなみに関数って「処理をまとめられるもの」っていうけど
「処理って、今回ひとつしかないじゃん!」
と思うでしょ?そうです。
このくらいならまとめる必要性がないんです。では、なぜ関数にしているのか?
次に出てくる「setInterval」というもので必要になるからです。
STEP2:setIntervalで◯秒毎に処理を実行する
「◯秒毎に何かしらの処理をする」というのは、jsに元からある「setInterval」というものを使います。
使い方はこうです。
1 |
setInterval(実行したい処理をまとめた関数名, 実行毎の秒数をミリ秒で指定); |
なので、今回は「0.1秒毎」に「whisperLoveという関数を実行したい(「愛してるよ。」と表示させたい)」ので
1 |
setInterval(whisperLove, 100); |
という書き方になりますね。
関数と引数について
ちなみにsetIntervalも「関数」なんです。
関数を実行する時(「関数を呼び出す。」って言い方をよくします)、そのカッコの中には、その関数へ「値」を渡すことができます。
関数へ渡す値のことを「引数」って呼びます。
引数は「,(カンマ)」をつけて複数渡す事ができます。
仕事で例えるなら、関数という人に「これとこれ使って仕事してくださいー」ってお願いしてる感じです。
なので、今回setIntervalという関数に引数を2つ渡してるわけですね。
「実行したい処理」と「実行する間隔」ですね。
そして、ここで大事なのはsetIntervalという関数に実行したい処理を渡すには
関数で渡さなければならない。
ということです。
なんで?って聞かないでくださいね。「そういう決まり」なんです。
なので、今回
1 |
document.write('愛してるよ。'); |
というたった1行の処理でまとめる必要もないものを
わざわざ関数にまとめた上で、setIntervalに引数として渡した。
ってことなんです。
そして、setIntervalの2つ目の引数に「実行する間隔」を数値型で100を渡していますね。
100ミリ秒で0.1秒になります。
0.1秒ごとにwhisperLoveという指定した関数が実行されます。
関数の中身は今回は
1 |
document.write('愛してるよ。'); |
なので、
0.1秒ごとに画面に「愛してるよ。」が表示され続ける。
というわけです。
今回は「ブラウザを閉じない限りは永遠と表示され続ける。」という彼女が見たらガクブルな状態ですが、ちゃんとsetIntervalを止める方法があります。
調べてみてください。
リファクタしてみよう!
またまたさっきのコードをリファクタやっていきましょう。
1 2 3 4 |
function whisperLove(){ document.write('愛してるよ。'); } setInterval(whisperLove, 100); |
ちなみに
どこがリファクタ出来るか分かりますか?
いやいや、たった4行なのにどこ変えるの!?
って思いますよね。でも、変えられます。
5分くらい考えてみてください。
はい。では、答えです。
実は、こうできます。
1 2 3 |
setInterval(function(){ document.write('愛してるよ。'); }, 100); |
え?
って感じですよね。パッと見どうなってるのか意味わからん感じです。
第一引数をよくみてください。
1 2 3 |
function(){ document.write('愛してるよ。'); } |
となってますね。
これって関数に似てません??
しかも「document.write(‘愛してるよ。’);」ってやつ入ってるし。
そうです。これ、「関数」です。
でも、
名前ないですよね??
「function」の後に名前つけるはずですよね??
functionのあとには、すぐカッコがついちゃってますね。
そう。これ、
「無名関数」
っていいます。
「名無しの関数」って意味です。
なにそれ?と思うでしょうが、そういうもんです。
関数なんですが、「名前がついてないだけ。」です。
普通の名前あり関数は例えるなら
「作り置きしておいたもの(関数)で、色々な場所で呼び出して使える」
ですが、無名関数は
「その場で作ったもの(関数)で、その時だけの使い捨て」
として使えるものです。
だって、名前ないんだもん。
名前ないのにどうやって呼び出すの?
ってことですね。
仕事でも「あいつ呼んできて」と上司に言われたら「は?(キレ気味)」ってなりますよね。
名前ない人を呼んでくることは出来ません。
その「無名関数」を第一引数に書いているわけですね。
分かり易いように改行してありますが、これも1行でこう書いたっていいわけです。
1 |
setInterval(function(){ document.write('愛してるよ。'); }, 100); |
でも、見難いのでやめましょう。
話戻って、思い出してください。
setIntervalという関数を実行する時に第一引数には
実行したい関数名を書く
んでしたね。
実は、これって関数名を第一引数に書くことで
「関数を渡している」
んです。
関数に関数を渡しているんです。
「関数に関数!?」って意味わからないでしょうが、そういうもんだと思うしかありません。
イメージ的には職場で
関数という人に「この人と一緒に仕事して」ともう一人の関数くんを紹介した
ってイメージです。
で、今回setIntervalの第一引数には
関数名を書く事で関数を渡している
わけですね。
なので、
いちいち関数作らなくても、こんな事も出来ちゃうわけです。
1 2 3 |
setInterval(function(){ document.write('愛してるよ。'); }, 100); |
第一引数に直接
関数を書いちゃう。
第一引数に渡す時に関数を作っちゃう。
ってことです。
イメージとしては、今までは
作り置きしたもの(whisperLove関数)を相手(setInterval関数)に渡す
ものを
その場で作って(whisperLove関数を)、相手(setInterval関数)に渡す
のか。
という感じです。職場の仕事で例えるなら
正社員(whisperLove関数くん)を雇って、同僚(setInterval関数)と仕事してもらう
のか
請負(whisperLove関数くん)を雇って、同僚(setInterval関数)と仕事してもらう
のか。
の違いです。
だって、
whisperLove関数は、一回しか仕事しない(使わない)んですよ?
1回しか仕事を頼まないのにあなたは、
「正社員を雇うんですか?」
ってことです。正社員雇ったら、首切るに切れません。
他にやってもらう仕事もないのにお給料払い続けるわけですね。
だったら、
スポットだけ仕事してもらえる請負でよくない??
ってことです。
もちろん、プログラミングの世界では、関数を作ったところで「お給料にあたるようなものを常に支払うことになる」なんてことはありません。
ですが、関数は「関数というもので処理をまとめて作り置きしておけば、色々な箇所で呼び出せる」のがメリットなわけなので
「1回しか使わないならわざわざ作り置きする必要ない」わけですね。
(実際、関数を作り置きするか、その場で作るか、で微妙に内部的な差があるんですが、今は気にしなくていいです。javascript・jQuery部中級でやるので。)
その場で雇って「こいつと仕事して」とその場の部下に充てがった。
ってな感じですね。
ただし、処理がめっちゃ長いのなら、関数にした方がいいです。
今回は関数内のコードが短いのでこうリファクタできる。ってだけなので。
もし、
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
setInterval(function (){ var w, h, wn, hn, ha, va, hdif, vdif, margT = 0, margL = 0, $imgCW = $imgBoxCont.width(), $imgCH = $imgBoxCont.height(); // Save original sizes if ($img.data('owidth') === undefined) $img.data('owidth', $img[0].width); if ($img.data('oheight') === undefined) $img.data('oheight', $img[0].height); // Compare ratio if (settings.fill === ($imgCW / $imgCH) >= ($img.data('owidth') / $img.data('oheight'))) { w = '100%'; h = 'auto'; wn = Math.floor($imgCW); hn = Math.floor($imgCW * ($img.data('oheight') / $img.data('owidth'))); } else { w = 'auto'; h = '100%'; wn = Math.floor($imgCH * ($img.data('owidth') / $img.data('oheight'))); hn = Math.floor($imgCH); } // Align X ha = settings.horizontalAlign.toLowerCase(); hdif = $imgCW - wn; if (ha === 'left') margL = 0; if (ha === 'center') margL = hdif * 0.5; if (ha === 'right') margL = hdif; if (ha.indexOf('%') !== -1){ ha = parseInt (ha.replace('%',''), 10); if (ha > 0) margL = hdif * ha * 0.01; } }, 100); |
みたいに関数内のコードが大量(例はテキトーなコードで意味はありません)にあったら見にくいですよね?
だったら、「関数にまとめて見やすくする」という事も必要になってきます。
もちろん、「どの程度のコード量だったら?」という線引きはないのでエンジニアや現場判断でバラバラですけどね。
鬼練7:「仕事と私」どっちを取るのよ!恐怖の男編(リファクタの極みver)
今回は、鬼練5のコードをただリファクタしてみたいと思います。とは言っても、かなり説明長いので覚悟してください。
せっかくなんで、もう1回1から書いてみましょう。
仕様書
下記要件に従ってプログラムを組んでみてください。
画面に
「彼女:仕事と私どっちを取るのよ!」という文字を表示させる
その次に
「自分:◯◯◯」を表示させる。
◯◯◯は変数(変数answerとする)にしておき、その変数の中身を表示させる。
変数answerには
「仕事に決まってるだろ!」
「仕事よりも君が大事さ。」
のどちらかの文字列をランダムで入れておく。
変数answerの中身が「仕事に決まってるだろ!」だった場合には、
「彼女:あたたたたたたたっーーー!!」と
「自分:ひでぶっ!!」という文字を表示させる
変数answerの中身が「仕事よりも君が大事さ。」だった場合には、
「自分:愛してるよ。」を100回表示させ、
「彼女:ひぃやぁぁぁーーーーーー!」と
「自分:ひでぶっ!!」という文字を表示させる
答え
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
var CHOICES = { YOUR: '仕事よりも君が大事さ。', WORK: '仕事に決まってるだろ!' }; var answer = (Math.floor(Math.random()*2)) ? CHOICES.YOUR : CHOICES.WORK; document.write('彼女:仕事と私どっちを取るのよ!<br>'); document.write('自分:' + answer + '<br>'); if(answer === CHOICES.WORK){ document.write('彼女:あたたたたたたたっーーー!!<br>'); } else if (answer === CHOICES.YOUR) { for(var i = 0; i < 100; i++){ document.write('自分:愛してるよ。<br>'); } document.write('彼女:ひぃやぁぁぁーーーーーー!!<br>'); } document.write('自分:ひでぶっ!!'); |
では、リファクタしていきましょう。
え?前にリファクタしたじゃん!って??
そうです。でも、まだまだリファクタの余地があります。
鬼練6で「関数」についてやりましたね?
その関数でまとめられませんか?さらに「引数」を使ったりして。
ちょっと考えてみましょう。
だって、もう1回最初か書いてみて、めんどくさくなかったですか?
「doc….」
なやつ。
はい。ってことで、リファクタ後をみてみましょう。
どんっ
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
var CHOICES = { YOUR: '仕事よりも君が大事さ。', WORK: '仕事に決まってるだろ!' }; var answer = (Math.floor(Math.random()*2)) ? CHOICES.YOUR : CHOICES.WORK; function sayWords(words, actorFlg){ var actorName = ''; switch (actorFlg){ case 0: // 彼女の場合 actorName = '彼女:'; break; case 1: // 自分の場合 actorName = '自分:'; break; default: } document.write(actorName + words); } sayWords('仕事と私どっちを取るのよ!<br>', 0); sayWords(answer + '<br>', 1); if(answer === CHOICES.WORK){ sayWords('あたたたたたたたっーーー!!<br>', 0); } else if (answer === CHOICES.YOUR) { for(var i = 0; i < 100; i++){ sayWords('愛してるよ。<br>', 1); } sayWords('ひぃやぁぁぁーーーーーー!!<br>', 0); } sayWords('ひでぶっ!!', 1); |
いやいやいやいやいや!
コード量増えてるやーーーーん!!
リファクタするって言っときながら、
きれいきれいできてませんやーーーん!!
ってツッコミたくなる気持ちはよく分かります。
ですが、決して
「コード量を少なくする」ことがリファクタではありません。
あくまで、
「他のエンジニアでもパッとみて分かり易いコード」
「あとあとで修正しやすい(テストも少なくて済む)コード」
がリファクタなんですからね。
なので、時に「あとあとで修正しやすい」ようにするために
「コード量が多くなる(ファイル数も多くなったりする)」
ということは多々あるわけです。ここらへん勘違いしないようにしてください。
STEP1:関数にまとめよう
エンジニアに大切なスキルは「面倒くさがり」です。
優秀なエンジニアほど実は「面倒臭がり」なんです。
だって、
1 |
document.write.... |
って毎回書くのめんどいでしょ??
そういう気持ちがまず生まれないと「ヤバイ」わけですね。
「document.writeを100回書いてください。」
って言われたら、
「めんどくせ!自動化させよ!for文でやった方がいいっしょ!関数にまとめるか!」
ってならないといけないわけですね。(実際はもっと高度なレベルでやるわけですが)
なので、
documentなんちゃらと毎回書いてるところをまとめましょう。
今回
「sayWords」
という関数にまとめました。「言葉を発する」的な英語ですね。
命名規則を考えよう
ちなみに「関数名」は「動詞」から始めるのが慣例です。
「関数を呼び出す」ということは「何かしらのまとまった処理を実行する(action)」わけなので、
「動詞(action)」から初めてあげると「この関数は何をするものなのか?」が関数名だけで伝わり易いです。
なので、「言う(say)」「words(言葉を)」という関数名にしてるわけです。
こういう「名前の付け方のルール」を「命名規則」っていいます。
https://qiita.com/shimataro999/items/ef6cd838d56f1fe87015
https://qiita.com/KeithYokoma/items/2193cf79ba76563e3db6
https://qiita.com/Kunikata/items/0337c6744a7c8fbc1586
詳しくは上記とかみてください。命名規則を決めて統一しておくのが大事ですね。
ちなみに単語と単語の間に「_(アンダースコア)」をつけて「say_words」とする
「スネークケース」
という書き方と
さきほどのように「最初の単語の先頭を小文字」にして「2番目以降の単語を大文字」にするのを
「ロワーキャメルケース」
って呼びます。(「アッパーキャメルケース」という先頭の1文字目も大文字にする書き方もあります。)
分かりやすさのために書き方を統一した方がいいです。(定数なら全部大文字にしとくとかね。)
現場によってどの書き方を採用しているかコードの変数と関数の名前を見れば分かるので、それに合わせましょう。
「基本はその現場にあるコードの書き方に合わせる」
という事が大事です。
勝手に自分ルールで書く。(業界では「オレオレコーディング」って呼びます)
なんてやっちゃダメですからね。
(もちろん、そもそものコードがくっそ汚いので綺麗にして欲しいから既存のコードは真似て欲しくない。って現場もあるので、分からないうちはマネージャーなり周りのエンジニアにこそっと聞いてみましょう。)
昔はjsではスネークケースが多かったですが、今は変数や関数はロワーキャメルが主流な感じになってます。
ちなみに、関数名の付け方や変数名の付け方で「初心者かどうか」がモロバレするくらい大事なので今のうちから意識するようにしましょう。
引数を使って関数に値を渡そう
話戻って、
sayWordsという関数にまとめるはいいけど問題は
「セリフがそれぞれ違うよね?」ということですね。
そこで、関数で使える「引数」という機能を使うわけです。
値を関数に渡す事ができるものですね。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
function sayWords(words, actorFlg){ var actorName = ''; switch (actorFlg){ case 0: // 彼女の場合 actorName = '彼女:'; break; case 1: // 自分の場合 actorName = '自分:'; break; default: } document.write(actorName + words); } |
今回は、2つの引数を関数に渡しています。
関数sayWords側から見れば、2つの値(引数)を受け取って何かしら処理を始める。
ってことですね。
まず、1つ目がwordsという名前にしましたが「セリフ」ですね。
なので、例えばちょっとシンプルにして
1 2 3 |
function sayWords(words){ document.write(words); } |
のような関数であれば、
この関数を呼び出す時に
1 |
sayWords('彼女:仕事と私どっちを取るのよ!<br>'); |
みたいに引数にセリフを渡せば
関数内の引数words(関数内だけで使える変数のこと)にセリフが格納されるので、そいつを使って
1 |
document.write(words); |
ってやるだけで、その引数の中身を表示させることができますよね??
こうすれば、毎回各箇所で「document.write〜〜」って書く必要がなくなります。
え?
コピーして貼り付けてるからそんな手間じゃないって??
。。。。
バカヤロウ!
その通りだよ!
そうです。もう、その通りなんです。
いちいち、document.writeをキーボードで毎回打ってるなんてもうバカヤロウです。
言ったでしょ?面倒くさがれ。って。
コピーすりゃいいじゃない。打ち間違いも減らせますよね?
もちろん、このdocument.write()って書く必要がなくなる。って程度であれば関数にわざわざする必要も実はないんです。
各箇所でdocument.write()使ってくださいよ。それくらい。
コピーすりゃいいじゃない。そのくらいなら。1行なんだから。
「コピペエンジニア(コードの中身は何やってるか分からないけど既存のコードをコピーして切りはりしてなんとか動くものが作れる現役でも結構な数いるエンジニアのこと)」
なんて呼ばれませんから大丈夫です。
実は、関数にまとめるメリットは今回ここではないんです。
STEP2:関数の引数で役者を変更しよう
大きなメリットはこの関数の第二引数にあります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
function sayWords(words, actorFlg){ var actorName = ''; switch (actorFlg){ case 0: // 彼女の場合 actorName = '彼女:'; break; case 1: // 自分の場合 actorName = '自分:'; break; default: } document.write(actorName + words); } |
「actorFlg」ですね。
actorは役者って意味でつけてます。
ちなみに「Flg」ってありますが「flag(フラグ)」の省略の意味で、プログラミング界ではよく使われます。
Boolean型のtrueまたはfalseを入れる(もしくは、数値型の0か1を入れる)ための変数という意味でよく使われます。
「フラグ」なので「フラグ(旗)が上がっているか、下がっているか」しかないですよ〜というのが、この変数名で分かります。
(こうやって変数名を決めておくことで実際に中に何が入っているか?入っていくのか?が見通せやすくなりますね)
今回は役者は「彼女」と「自分」の2人ですね。
trueとfalseも2択ですね。彼女と自分も2択です。
なので、フラグにしました。(値は0と1ではなく、trueとfalseでやってもいいです。本来フラグはtrueかfalseを入れる事がほとんどなので。)
分岐が多いならswitchを使おう
4行目からswitchというどのプログラミング言語にもよくある「分岐処理」を使っています。
分岐と言えば、「if文」をやりましたね。
他にも同じように分岐処理できる書き方で「switch文」というものがあります。
「この場合はこの処理をして、こっちの場合はこの処理をして〜」と
出来る事は全く同じで、書き方が違うだけです。
switch文の書き方はこうです。
1 2 3 4 5 6 7 8 9 10 |
switch (判定したい式や値){ case 値A: // いろいろな処理A break; case 値B: // いろいろな処理B break; default: // 条件に何もあてはまらない時の処理 } |
if文で同じように書くとこうなります。
1 2 3 4 5 6 7 |
if(判定したい式や値 === 値A){ // いろいろな処理A }else if(判定したい式や値 === 値B){ // いろいろな処理B }else{ // 条件に何もあてはまらない時の処理 } |
説明しなくてもなんとなく分かりますよね?
どう使えばいいかって。
defaultがif文でいうところのelseの部分になりますね。
switch文で気をつけないといけないのは
「defaultは一番最後に書くこと」
「caseのそれぞれの条件の処理の最後にはbreak;を必ずつけること」
です。
break;をつけないと条件に合致していない別のcaseの処理も実行されちゃいます。
「if-else-if〜〜〜」とelse-ifをめっちゃ書いて分岐させるよりかはswitch文を使う方がスマートに見やすく書けます。
(今回程度の分岐なら、switch文でなくてもいいですが紹介したかったんで。)
今回、このswitch文を使って「役割」によってセリフの役者表記の部分(「彼女:」と「自分:」のところ)を変えています。
1 2 3 4 5 6 7 8 9 10 |
switch (actorFlg){ case 0: // 彼女の場合 actorName = '彼女:'; break; case 1: // 自分の場合 actorName = '自分:'; break; default: } |
actorFlgが0なら「彼女:」を関数内で作った変数actorNameに入れてます。
actorFlgが1なら「自分:」を入れてますね。
それ以外なら「何もいれない(処理は何もしない)」です。
最後に
1 |
document.write(actorName + words); |
で、それをつなぎ合わせた文字列を表示したわけですね。
実際に関数を呼び出す箇所では
1 |
sayWords('仕事と私どっちを取るのよ!<br>', 0); |
と第二引数に0か1を渡してやるだけです。
なぜ、これがいいのか??
というと
「あとあとで修正しやすい(保守性が高い)」
からです。
あとで役者の名前が変わった場合を考えてみよう
例えば、
役者を「自分:」から「彼氏:」の表記に変えたい場合どうしましょ?
関数にまとめてなければ、全部のdocument.write()を書いた箇所で修正する必要がありますね?
何度も言ってますが、
この例程度のコードなら関係ありませんが、コードがもっと多くなったらどうですか?
ファイル数が100ファイルとかあって、それぞれのファイルにdocument.write()で書かれてたらどうです?
一個一個ファイルを開いてはファイル内のそれぞれの箇所を修正し、ファイルを閉じて〜を繰り返しますか?
え?
エディターには「ファイルをまたいで一括置換」ていう便利な機能があるって?
。。。。
バカヤロウ!
その通りだよ!
そう。またまたその通り。
一括置換でいけます。(一括置換を知らない人は調べてね)
たぶんね。
「たぶん。」と言ったのは、例えば100ファイルあってその中の大量のコードの中に
1 |
document.write('自分:〜〜〜〜'); |
と何箇所か書いてあったとしましょう。
でも、一部は
1 |
document.write('自分:〜〜自分だって〜〜'); |
となってたらどうです?
あれ、一括置換だと実際のセリフも変わっちゃいますね。
バグの発生です。
じゃあ、
1 |
document.write('自分: |
と始まる文字列に合致するものを
1 |
document.write('彼氏: |
と変えるように一括置換すればいいって??
だとしたら、
1 |
document.write('自分のような〜〜〜 |
ともしも「役者表記がない箇所」があったとしたら不味いですね。
そのルールで置換しようとするとルールに合致してしまうわけなので、置き換えられてしまいますね。
もちろん、それは実際の現場によるので、そういった箇所はないかもしれません。
実際の現場は開発当初のエンジニアメンバーはすでにいないところがほとんどです。
なので、
「誰も全体の詳しいコードは分からない(どこに何が書かれているか)」
のが普通です。
ということは、間違って合致しちゃうものがあるのか?ないのか?
「影響調査」
をする必要があるわけです。(影響調査については別のところで解説しているので省略します)
結構、この調査が時間かかる(金がかかる)わけですね。
ゆくゆくの将来、そんなことになってしまうのであれば、
関数にしちゃいましょうよ
ってことなんですね。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
function sayWords(words, actorFlg){ var actorName = ''; switch (actorFlg){ case 0: // 彼女の場合 actorName = '彼女:'; break; case 1: // 自分の場合 actorName = '自分:'; break; default: } document.write(actorName + words); } |
こうすれば、「sayWords関数内の一箇所を変えるだけ」で済みますからね。
一箇所しか変えてないなら、その箇所だけテスト(正常に動くか)すればいいですよね?
もし、
1000箇所を一括置換したらどうなりますか??
通常なら「全箇所が一括置換されていることを確認する」必要があるわけです。
もちろん、そこらへん現場や状況によって判断が様々です。
「一括置換っていう便利な間違いようもないツール使ってるんだから、何箇所か見てみて置き換わっていればおっけーっしょ!」
っていうフランクな現場もあれば、金融機関のように
「絶対に間違いは許されるわけがない!ツールであっても、修正したのであればその箇所は全部確認する必要があるのだ!置き換わってはいけない箇所が置き換わっている可能性もあるのだから、テストするのだ!」
という現場もあわけですね。
だったら、
「sayWords関数内の一箇所を変えるだけにしない?」
ってことですね。もちろん、そのリファクタをするのにめっちゃ時間かかるならまた判断が変わってくるでしょう。
あとで役者が増えた場合を考えてみよう
また話戻って
リファクタによってswitch文にしたことで
「あとあとで役者を追加できる」
というメリットもあります。例えば、「父親」が加わるとかですね。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
switch (actorFlg){ case 0: // 彼女の場合 actorName = '彼女:'; break; case 1: // 自分の場合 actorName = '自分:'; break; case 2: // 父親の場合 actorName = '父親:'; break; default: } |
こうなってくるとactorFlgも2種類の値じゃなくなってるので変数名を変えてあげた方が(そこもリファクタした方が)いいですね。
さらに
1 |
sayWords('あたたたたたたたっーーー!!<br>', 1); |
と呼び出せば、役者の「自分」が「あたたたたたたたっーーー!!」って言えるようになります。(今回は必要ないですが、ゆくゆくは「自分:あたたたたたたたっーーー!!」と表示したい。なんて事があるかもしれません。)
ただ、こういったリファクタですが注意すべきは
「そもそも、あとあとで変わる可能性はあるの?」
ってことです。
確実に「彼女」と「自分」という表記が変わることはない。
確実に役者が加わることはない。
のであれば、そもそもやる必要ないですからね。
「書く必要のないコードを書く」
のはお金の無駄遣いなので注意しましょう。
実際の実務ではこの「あとあと修正する可能性あるか?」「どんな可能性があるか?」が予想しづらかったり微妙なラインだったりするものがあるので、その都度現役エンジニアも頭を悩ませているわけです。
(悩んで時間をかけた方がいい現場(いい状況)なのか、とりあえず悩む時間かけるなら動くコード書いた方がいいのかは現場によるので、周りに聞いたり空気読みましょう)
さらにリファクタするなら
さらに突き詰めるなら「:(コロン)」も無駄じゃないですか??
2箇所で出てきちゃってますよね?
もしかしたら「:」が「|(パイプライン)」に変わるかもしれません。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
function sayWords(words, actorFlg){ var actorName = ''; var SYMBOL = ':'; switch (actorFlg){ case 0: // 彼女の場合 actorName = '彼女' + SYMBOL; break; case 1: // 自分の場合 actorName = '自分' + SYMBOL; break; default: } document.write(actorName + words); } |
こうやって、変数にした方がいいかもしれませんね。
「彼女」や「自分」もベタ書きするよりは、変数にした方がいいですね。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
function sayWords(words, actorFlg){ var actorName = ''; var SYMBOL = ':'; var ACTORES = { HE: '自分', HER: '彼女' }; switch (actorFlg){ case 0: // 彼女の場合 actorName = ACTORES.HER + SYMBOL; break; case 1: // 自分の場合 actorName = ACTORES.HE + SYMBOL; break; default: } document.write(actorName + words); } |
最終的には
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
var CHOICES = { YOUR: '仕事よりも君が大事さ。', WORK: '仕事に決まってるだろ!' }; var answer = (Math.floor(Math.random()*2)) ? CHOICES.YOUR : CHOICES.WORK; function sayWords(words, actorFlg){ var actorName = ''; var SYMBOL = ':'; var ACTORES = { HE: '自分', HER: '彼女' }; switch (actorFlg){ case 0: // 彼女の場合 actorName = ACTORES.HER + SYMBOL; break; case 1: // 自分の場合 actorName = ACTORES.HE + SYMBOL; break; default: } document.write(actorName + words); } sayWords('仕事と私どっちを取るのよ!<br>', 0); sayWords(answer + '<br>', 1); if(answer === CHOICES.WORK){ sayWords('あたたたたたたたっーーー!!<br>', 0); } else if (answer === CHOICES.YOUR) { for(var i = 0; i < 100; i++){ sayWords('愛してるよ。<br>', 1); } sayWords('ひぃやぁぁぁーーーーーー!!<br>', 0); } sayWords('ひでぶっ!!', 1); |
という感じです。
まだまだまだまだぁ!!!!
最終的にとか言いましたが、ホントはまだまだできます。
もうちょいなんで、頑張りましょう。
まず、
htmlのbrタグが「冗長(じょうちょう)的」です。
(冗長ってのは「無駄」って意味で、業界ではよく使う言葉なので使ってください。「ここらへんのコードって冗長的だよねー」って。)
そして、
「あたたたたたたたっーーー!!」「ひぃやぁぁぁーーーーーー!!」などセリフも使いまわせるようにセリフ定数(処理中に中身が変わることはないから定数がいい)WORDSといったものを作って入れてあげましょう。
最後に
sayWords関数の第二引数に0や1で値を渡すのは分かりにくいのでACTORESという定数に「自分」「彼女」を入れて使うようにしてあげましょう。
そうするとホントの最終的にこうなります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
var CHOICES = { YOUR: '仕事よりも君が大事さ。', WORK: '仕事に決まってるだろ!' }; var WORDS = { CHOICE: '仕事と私どっちを取るのよ!', ATTACK: 'あたたたたたたたっーーー!!', SCREAM: 'ひぃやぁぁぁーーーーーー!!', LOVE: '愛してるよ。', DEAD: 'ひでぶっ!!' } var ACTORES = { HE: '自分', SHE: '彼女' }; var answer = (Math.floor(Math.random()*2)) ? CHOICES.YOUR : CHOICES.WORK; function sayWords(words, actorFlg){ var actorName = ''; var SYMBOL = ':'; switch (actorFlg){ case ACTORES.SHE: // 彼女の場合 actorName = ACTORES.SHE + SYMBOL; break; case ACTORES.HE: // 自分の場合 actorName = ACTORES.HE + SYMBOL; break; default: } document.write(actorName + words + '<br>'); } sayWords(WORDS.CHOICE, ACTORES.SHE); sayWords(answer, ACTORES.HE); if(answer === CHOICES.WORK){ sayWords(WORDS.ATTACK, ACTORES.SHE); } else if (answer === CHOICES.YOUR) { for(var i = 0; i < 100; i++){ sayWords(WORDS.LOVE, ACTORES.HE); } sayWords(WORDS.SCREAM, ACTORES.SHE); } sayWords(WORDS.DEAD, ACTORES.HE); |
だいぶスッキリしましたね。
え?
逆に混乱するって??
そうです。
初心者はそうなんですよ。
でも、コードに慣れたエンジニアは絶対にこっちの方が「見やすい」し、「あとあと修正しやすいな」って見て分かります。
(見やすい、修正しやすい事をエンジニアは「コードが綺麗」って言います。)
もちろん、あとあとで変更する可能性がまずもってないとか使い回すこともない。なら不要ですけどね。
でも、大抵の場合は変更する可能性はまずない。と言ってもそれは「今の時点」での話で、何年後どうなるかは実際分かりませんね。
この例題なら「小説」のようなサイト?システム?なイメージなので、他の役者が出てくるかもしれません。
「あとあと修正しやすいように書く(設計する。と言ったりもします)」のにすごい時間がかかるのであれば、変更される可能性の低いものに時間をかけてまで「保守性高く」するのは「無駄」になりますが、この程度のコード量だった場合はどっちで書いてもそう時間はかからない(初心者はかかるでしょうけどね)ので、最初から保守性高いコードで書いてしまえばいいでしょうね。(正解があるわけじゃありませんが書けるに越したことはありません。)
だから、このくらいリファクタされたコードに今のうちから慣れていってください。(javascript部やPHP部でもサンプルコードにはリファクタの余地をいっぱい残してますので、余裕あれば自分でリファクタしてみましょう)
ちなみにコードの書き順は
- 定数定義
- 変数定義
- 関数定義
- 実際の処理
という順序にしてあげるのが実務では慣習になっています。(見やすいですからね)
リファクタの極み
実はもう2箇所ホントはリファクタ出来る余地があります。
えーーーーー!まだあんの!?
って思いますよね。すいませんね。
でも、分かりますか??
見直してみてください。なんか、同じ感じの意味合いっぽい変数ないですかね??
同じグループなんじゃね??ってやつ。
そう、これです。
1 2 3 4 5 6 7 8 9 10 11 |
var CHOICES = { YOUR: '仕事よりも君が大事さ。', WORK: '仕事に決まってるだろ!' }; var WORDS = { CHOICE: '仕事と私どっちを取るのよ!', ATTACK: 'あたたたたたたたっーーー!!', SCREAM: 'ひぃやぁぁぁーーーーーー!!', LOVE: '愛してるよ。', DEAD: 'ひでぶっ!!' } |
選択肢という意味の定数CHOICESですが、これって要は
「セリフ」
ですよね??
まとめられそうですね。やってみましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
var WORDS = { YOUR: '仕事よりも君が大事さ。', WORK: '仕事に決まってるだろ!', CHOICE: '仕事と私どっちを取るのよ!', ATTACK: 'あたたたたたたたっーーー!!', SCREAM: 'ひぃやぁぁぁーーーーーー!!', LOVE: '愛してるよ。', DEAD: 'ひでぶっ!!' } var ACTORES = { HE: '自分', SHE: '彼女' }; var answer = (Math.floor(Math.random()*2)) ? WORDS.YOUR : WORDS.WORK; |
こうしちゃえばいいですよね。
もう1つは、「使っていない分岐」ありませんか??
ほら、何の処理もしてないのに分岐してますよね?
分かりませんか?
1 2 3 4 5 6 7 8 9 |
switch (actorFlg){ case ACTORES.SHE: // 彼女の場合 actorName = ACTORES.SHE + SYMBOL; break; case ACTORES.HE: // 自分の場合 actorName = ACTORES.HE + SYMBOL; break; default: } |
defaultっていります?
if文でいうところの「else」の条件ですね。
今回何もしてないので、「それ以外」自体の分岐が必要ないので
1 2 3 4 5 6 7 |
switch (actorFlg){ case ACTORES.SHE: // 彼女の場合 actorName = ACTORES.SHE + SYMBOL; break; case ACTORES.HE: // 自分の場合 actorName = ACTORES.HE + SYMBOL; } |
でいいんです。
2番目の「case」の最後についてた「break;」も必要なくなります。
2番目の「case」以降に分岐が続いているわけじゃないので、そっちの処理に入るのを止めるための「break;」も必要ないってことです。
なので、ホントのホントの最終的に
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
var WORDS = { YOUR: '仕事よりも君が大事さ。', WORK: '仕事に決まってるだろ!', CHOICE: '仕事と私どっちを取るのよ!', ATTACK: 'あたたたたたたたっーーー!!', SCREAM: 'ひぃやぁぁぁーーーーーー!!', LOVE: '愛してるよ。', DEAD: 'ひでぶっ!!' } var ACTORES = { HE: '自分', SHE: '彼女' }; var answer = (Math.floor(Math.random()*2)) ? WORDS.YOUR : WORDS.WORK; function sayWords(words, actorFlg){ var actorName = ''; var SYMBOL = ':'; switch (actorFlg){ case ACTORES.SHE: // 彼女の場合 actorName = ACTORES.SHE + SYMBOL; break; case ACTORES.HE: // 自分の場合 actorName = ACTORES.HE + SYMBOL; } document.write(actorName + words + '<br>'); } sayWords(WORDS.CHOICE, ACTORES.SHE); sayWords(answer, ACTORES.HE); if(answer === WORDS.WORK){ sayWords(WORDS.ATTACK, ACTORES.SHE); } else if (answer === WORDS.YOUR) { for(var i = 0; i < 100; i++){ sayWords(WORDS.LOVE, ACTORES.HE); } sayWords(WORDS.SCREAM, ACTORES.SHE); } sayWords(WORDS.DEAD, ACTORES.HE); |
こうですね。
これで、やっとこの鬼練7は終了になります。
このリファクタに関しては最初からここまで出来るわけじゃありませんし、最初から理解できるわけでもないので安心してください。
実務でコードに毎日にように触れないと理解できない人もいますからね。
ただ、目指していくところはここだ。ってことを知っておいてください。
つづく。。