【脱・初心者🔰】スパゲッティコードを生まないコーディング術5選!【JavaScript】
どうもお久しぶりです、Keshitanです!
皆さんはプログラムを書いていて、「どこがどうなって、何が起こっているか、ワッッッかんねー!!!」……となった経験、ありませんか?
そういったときのコードの様相とは、まぁ"想像を絶するもの"で、ぐっちゃぐちゃのスパゲッティコード🍝となっていますよね。。。最終的にはファイル📄をそっ閉じしてゴミ箱🗑️の彼方へ……『新規ファイルを作成』までが王道ではないでしょうか?
今回は、『【脱・初心者🔰】スパゲッティコードを生まないコーディング術5選!【JavaScript】』と題しまして、そんなスパゲッティコードと"おさらば"するための、JavaScript初学者必見の内容を集めてみました!
"スパゲッティコード"
🍝は何故生まれるのか?
「スパゲッティコードは何故生まれてしまう」のでしょうか?理由は十人十色、千差万別でしょうが、単純なものとしては煩雑さを回避する書き方を知らないからというのがあるのではないでしょうか?特に初学者からすると尚のことだと考えます。
上級プログラマーであれば必ずといって良いほど「リファクタリング」、つまりは「ソースコードの整理・整頓」を行っています。
この記事でいう「コーディング術」とは、言わばJavaScript版便利収納術であり、本記事の内容は一番取っ付きやすいものに絞って紹介しています。
🍝を許すな!
そういえば何故、スパゲッティコードを回避したいのでしょうか?もっと言えば何故スパゲッティコードは嫌悪されるのでしょうか。
それはバグ潰しやアプデ、仕様変更を行うときに、嫌というほど困った経験から来るものでしょう。理由は単純明快、「何が書いてあるか分からない!」これに尽きます。つまるところ、
ということなのです。
スパゲッティコードは、個人単位であれば"将来の自分"が、チーム単位であれば"チーム全員"が困り果て、生産性がダダ下がりとなります。コーディング術とはプログラミングにおける必須テクニックなのです。
脱・初心者🔰のコーディング術
💠脱・var宣言!
"var"宣言とは、これのこと:
Before
var xxx = 1234
var yyy = 9876
/* 処理 */
console.log(xxx, yyy)
その他の宣言方法はこんな感じ:
After
const xxx = 1234
let yyy = 9876
/* 処理 */
console.log(xxx, yyy)
つまり、言いたいことはそのまんま!
ver num = 15
という再代入・再宣言可能であるvar宣言をやめましょう!
ということです。では何故「var宣言はNG」なのか?それは全ての処理に目を通さないと値が確定しないからです。例えばBeforeの/* 処理 */が以下のような実装だとしましょう:
Beforeの/* 処理 */
xxx += 4321
var yyy = 6789
このときのBefore最終行console.log(xxx, yyy)による出力結果はどうなるでしょうか?
Beforeの出力結果
5555 6789
つまりはvar宣言は「自由に」再代入ができちゃうし、「自由に」再宣言もできちゃうしで、道中やりたい放題できてしまう宣言なのです。
道中やりたい放題できるとどうなるか?変数が想定通りの値であるか、「「「一見関係がなさそうな箇所でも」」」逐一読み進めないといけないのです。これでは非常にストレスが溜まりますし、可読性が著しく低いです。
それでは他方の宣言、let及びconstはどうでしょう。Afterの/* 処理 */が以下のような実装だとしましょう:
Afterの/* 処理 */
xxx += 4321
let yyy = 6789
すると出力結果はErrorになります。これは上の行も下の行もエラーです。つまりはlet宣言やconst宣言は、再代入や再宣言を制限し、値の確実性を担保してくれているのです。言い換えれば「うっかり代入」「うっかり宣言」を防ぎます。
また、値の確実性という観点では、
const>>>>(越えられない壁)>>>>let>>>var
であるため、原則定数(const)宣言で、最終手段としてletを用いましょう。各種宣言の違いについて、表形式でまとめておきます:
$$
\begin{array}{c|ccc}
& \mathrm{const} & \mathrm{let} & \mathrm{var} \\ \hline
再代入 & ✕ & 〇 & 〇 \\
再宣言 & ✕ & ✕ & 〇
\end{array}
$$
⠀
⠀
⠀
⠀
⠀
💠脱・連結演算子!
"連結"演算子ってありましたっけ。あぁ、こういうの?:
Before
const unit = 100_000
const populations = {
"JPN": 1_244 * unit,
"USA": 3_400 * unit,
"BRA": 2_164 * unit,
"GBR": 677 * unit,
"EGY": 1_127 * unit,
"AUS": 264 * unit,
}
const question =
"ブラジルの総人口は、日本と豪州と英国の総人口よりも多い。〇か✕か。"
const answer =
"✕:ブラジルの総人口は" + populations.BRA + "人である一方、\n"
+ "日本・豪州・英国は" + populations.JPN + populations.AUS + populations.GBR + "人である。"
console.log(question)
console.log(answer)
「+」祭りで読みにくいコードだなぁ…。これを読みやすく変えるとこんな感じ:
After
const unit = 100_000
const populations = {
"JPN": 1_244 * unit,
"USA": 3_400 * unit,
"BRA": 2_164 * unit,
"GBR": 677 * unit,
"EGY": 1_127 * unit,
"AUS": 264 * unit,
}
const question =
"ブラジルの総人口は、日本と豪州と英国の総人口よりも多い。〇か✕か。"
const answer =
`✕:ブラジルの総人口は${populations.BRA}人である一方、
日本・豪州・英国は${populations.JPN + populations.AUS + populations.GBR}人である。`
console.log(question)
console.log(answer)
つまり、ここでの主張はこういうこと。
const str = "合計:" + a + b
という「+」を使った文字列の結合をやめましょう!
ということです。なぜ文字列の"結合"としての「+」を利用してはいけないのか?それはBeforeを実際に実行してみれば分かります:
Beforeの実行結果
ブラジルの総人口は、日本と豪州と英国の総人口よりも多い。〇か✕か。
✕:ブラジルの総人口は216400000人である一方、
日本・豪州・英国は1244000002640000067700000人である。
……はい。桁数がバクり散らかしました。これでは、人口の超新星爆発で食糧危機どころではないですね。
なぜこのようになるか?理由は単純で、暗黙の型変換が行われたからです。Beforeで言えば、
// 文字列 + 数値 -> 連結
"日本・豪州・英国は" + populations.JPN
//"日本・豪州・英国は124400000"
となり、知らず知らずのうちに数値であるpopulations.JPNが文字列に変換されています。後続の総人口数も同様に変換されて意図しない結果となります。
一方で、Afterはテンプレートリテラル表記を用いることで、正確性や可読性が向上しています:
`✕:ブラジルの総人口は${populations.BRA}人である一方、
日本・豪州・英国は${populations.JPN + populations.AUS + populations.GBR}人である。`
//✕:ブラジルの総人口は216400000人である一方、
//日本・豪州・英国は218500000人である。
テンプレートリテラルは「` `」(バッククォート)で囲み、埋め込みたい箇所を「${ }」で指定するだけです。たったこれだけで得られる恩恵は非常に大きいので、是非ともマスターしたいですね。
⠀
⠀
⠀
⠀
💠脱・マジックナンバー!
"マジック"ナンバー??そんなのあるんですか!え、どれのこと?:
Before
const inputYourGenderCode = 2
if (inputYourGenderCode === 1) {
/* 処理 */
} else if (inputYourGenderCode === 2) {
/* 処理 */
} else {
/* 処理 */
}
何がマジックなんだか不明なんですけど!でも、言いたいことは"コレ"で何となく分かるはずです:
After
const genderDict = {
"unknown": 0,
"male": 1,
"female": 2,
"other": 9
}
const inputYourGenderCode = 2
const isMale = inputYourGenderCode === genderDict.male
const isFemale = inputYourGenderCode === genderDict.female
if (isMale) {
/* 処理 */
} else if (isFemale) {
/* 処理 */
} else {
/* 処理 */
}
さぁどうでしょう、結構違いますね。その中でも特に主張したいこと、それは…
if (statusCode === 404) {
/* 処理 */
}
という"状態"を表す定数のべた書きをやめましょう!
ということです。なぜ「状態を表す定数のべた書き」はNGなのか?それは第三者からすれば暗号でしかないからです。Beforeにツッコミを入れると、こういうこと:
const inputYourGenderCode = 2
// 「1」に等しいから何?
if (inputYourGenderCode === 1) {
/* 処理 */
// 「2」に等しいと何をする??
} else if (inputYourGenderCode === 2) {
/* 処理 */
// 「それ以外」って何???
} else {
/* 処理 */
}
第三者目線、不親切の極みですね。これを目に優しくしたAfterは、このように読めるでしょう:
// 性別と数値を対応付けているのか。
const genderDict = {
"unknown": 0,
"male": 1,
"female": 2,
"other": 9
}
const inputYourGenderCode = 2
// 性別コードに合致するか見ているな。
const isMale = inputYourGenderCode === genderDict.male
const isFemale = inputYourGenderCode === genderDict.female
// 最初は男性の場合で、
if (isMale) {
/* 処理 */
// 次に女性の場合。
} else if (isFemale) {
/* 処理 */
// 最後はその他の場合か。
} else {
/* 処理 */
}
第三者が読んで意図の分からないコードは、忘れた頃の貴方も困り果て、しまいには「誰だ!こんなスパゲッティコード🍝を書いたのは!!!!」と言ってファイル📄をゴミ箱🗑️に放り投げることでしょう。親切な書き方は、記述量の増加を招きますが、可読性の圧倒的な向上によりQoLは爆上がりです。
マジックナンバーは経験則的にif文で見られがちです。定数(const)宣言を活用して初心者を脱していきましょう!
⠀
⠀
⠀
⠀
⠀
💠脱・古典的forループ!
"古典的"forループ??なんすか古典的って。例えばこんなヤツ:
Before
const blackList = ["xxxxxxxxxxx", /* ..., */ "xxxxxxxxxx"]
let phoneNumberList = ["xxx-xxxx-xxxx", /* ..., */ "xxxx-xx-xxxx"]
for (let i = 0; i < phoneNumberList.length; i++) {
phoneNumberList[i] = phoneNumberList[i].replace(/-/g, "")
}
for (let i = 0; i < phoneNumberList.length; i++) {
if (blackList.includes(phoneNumberList[i])) {
console.log(`🚫NG: ${phoneNumberList[i]}`)
} else {
console.log(`✅OK: ${phoneNumberList[i]}`)
}
}
for文がいっぱいで、うーん。。。これは読む気失せますねぇ。これを「脱・初心者🔰」するとこんな感じ:
After
const blackList = ["xxxxxxxxxxx", /* ..., */ "xxxxxxxxxx"]
const phoneNumberList = [
"xxx-xxxx-xxxx",
/* ..., */
"xxxx-xx-xxxx"
].map(phoneNumber => phoneNumber.replace(/-/g, ""))
phoneNumberList.forEach(phoneNumber => {
if (blackList.includes(phoneNumber)) {
console.log(`🚫NG: ${phoneNumber}`)
} else {
console.log(`✅OK: ${phoneNumber}`)
}
})
この例で何を伝えたいかお分かりでしょうか??そうです。
for (let i = 0; i < array.length; i++) {
/* 処理 */
}
という"古典的な"for文の書き方をやめましょう!
ということです。「なぜ古典的か?」という疑問は後に回すとして、ここではひとまず、「古典的=手続き的」としましょう。
古典的なfor文とは所謂、手続き的な書き方というもので、分かりやすく言うなれば「むかしむかしある所に…」と1つ1つ事柄を列挙するイメージです。つまりはBeforeでは、こういうこと:
let phoneNumberList = ["xxx-xxxx-xxxx", /* ..., */ "xxxx-xx-xxxx"]
// 回します、iは最初0で、iはphoneNumberList.length未満で、iは1ずつ増加
for (let i = 0; i < phoneNumberList.length; i++) {
// phoneNumberListのi番目要素はハイフン(-)を除去されます
phoneNumberList[i] = phoneNumberList[i].replace(/-/g, "")
}
一方、手続き的ではない方、ここでは「宣言的」としましょうか。Afterでは、このように読めます:
const phoneNumberList = [
"xxx-xxxx-xxxx",
/* ..., */
"xxxx-xx-xxxx"
// phoneNumberListは上記の各要素からハイフン(-)を除去した配列
].map(phoneNumber => phoneNumber.replace(/-/g, ""))
ここでのポイントはAfterを日本語化したときに、ほぼ一文で説明ができる内容だということです。それはmethodが動詞で命名されることから容易に分かります。言ってしまえば英文の構造と同様で、
と考えられます。主語、動詞、、、と来れば99.9%の確率で一文として完結するでしょう。methodは簡潔に書く能力に優れます(Wミーニング、ここで爆笑)。
また、古典的なfor文では書き分けられなかった目的の違いもメソッドにより表現できます。.mapを読む時点で「forはforでも配列が欲しいんだな」というのが見て取れ、.forEachを読む時点で「とりあえずこの配列の要素を回していきたいんだな」と意図が明瞭に分かります。
つまるところ、脱・初心者の近道はArrayのメソッドを知るところにあるかもしれません。
⠀
⠀
⠀
⠀
⠀
💠脱・破壊メソッド!
メソッド教信者の皆様、一つ謝らせて下さい。"破壊"メソッド信者は異教徒です:
Before
const piNumbers = [ 3, 1, 4, 1, 5, 9, 2 ]
const piMainNumbers = items.splice(0, 4)
console.log(piMainNumbers, piNumbers)
ちょっと過激派ですか?でも次のコードを読めば分かるはずです:
After
const piNumbers = [ 3, 1, 4, 1, 5, 9, 2 ]
const piMainNumbers = items.slice(0, 4)
console.log(piMainNumbers, piNumbers)
ん???よく分からない???違いはメソッドがsplice->sliceとなっているところですよ。言いたいことはこういういうことで、
items.splice(0, 4)
や、
items.reverse()
といった"破壊"メソッドの使用をやめましょう!
ということです。ただのメソッドに過ぎないこれらが何故"破壊的"であるのか?それはBeforeの実行結果と、Afterの実行結果の比較により判明します:
Beforeの実行結果
[ 3, 1, 4 ] [ 1, 5, 9, 2 ]
Afterの実行結果
[ 3, 1, 4 ] [ 3, 1, 4, 1, 5, 9, 2 ]
つまりは、メソッドspliceとsliceは共に「配列の一部を返す」という点では同一であるが、spliceは同時に「元の配列に変更を加えてしまう」ものであったということです。言わば、
Before第2行
const piMainNumbers = items.splice(0, 4)
という1つの宣言には、2つの操作が混在していたということになります。勿論、これにより可読性は下がります。実質的に再代入となんら変わりがないわけなので。
従って、メソッドは簡便な表現ですが、そのメソッドが「「「破壊的であるか否か」」」という視点は使う上で重要です!そして、使用はできるだけ避け、非破壊メソッドを利用しましょう!
⠀
⠀
⠀
⠀
⠀
⠀
おまけ
今回選ばせて頂いた脱・初心者のためのコーディング術5選は、「宣言->演算子->if文->for文->メソッド」という初学者が学ぶであろうガイドに沿ってピックアップしました。個人的には選りすぐりのものを選んだつもりですが、参考になりましたでしょうか。
5選の内容には含みませんでしたが、一番のコーディング術は"休息を取ること"コレ一択です。最新の研究でもヒトの脳は休息時にタスクの整理を行うそうで、騙されたと思って休んでみてください。一番効きます!!!
こんな感じでゲームやプログラミングを中心としてnote記事を執筆していますので、もし気に入って頂けましたらスキ・フォロー・コメントをして頂けると幸いです!
それではまたの機会に。