Day21 後半のゴール
前半では「1つの関数に全部書かない」「役割ごとに分ける」という感覚を掴みました。
後半では、それをもう一歩進めて、
少し大きめの処理をどう分解するか
「どこまで分けるか」の判断基準
関数同士の関係をどう整理するか
を、具体的な例を通してじっくり見ていきます。
ここからが「大規模化の基礎」の本番です。
例題の全体像をまず眺める
テーマ:プロフィールフォーム+localStorage
次のような小さなアプリを考えます。
名前とメールアドレスを入力するフォーム
入力チェック(必須・形式チェック)
エラー表示
localStorage への保存
ページ読み込み時の復元
これを「関数分割なし」で書くと、かなりゴチャつきます。
まずは、あえて「よくあるベタ書き版」を見てみましょう。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Day21 関数分割 後半</title>
</head>
<body>
<h1>プロフィール設定</h1>
<p>名前</p>
<input id="nameInput" type="text">
<p>メールアドレス</p>
<input id="emailInput" type="text">
<p>
<button id="saveButton">保存</button>
</p>
<p id="message"></p>
<script>
const nameInput = document.getElementById("nameInput");
const emailInput = document.getElementById("emailInput");
const saveButton = document.getElementById("saveButton");
const message = document.getElementById("message");
const savedProfileText = localStorage.getItem("profile");
if (savedProfileText !== null) {
const savedProfile = JSON.parse(savedProfileText);
nameInput.value = savedProfile.name;
emailInput.value = savedProfile.email;
}
saveButton.addEventListener("click", () => {
const name = nameInput.value.trim();
const email = emailInput.value.trim();
if (name === "") {
message.textContent = "名前を入力してください。";
message.style.color = "red";
nameInput.style.border = "2px solid red";
emailInput.style.border = "1px solid #ccc";
return;
}
if (email === "") {
message.textContent = "メールアドレスを入力してください。";
message.style.color = "red";
nameInput.style.border = "1px solid #ccc";
emailInput.style.border = "2px solid red";
return;
}
if (!email.includes("@")) {
message.textContent = "メールアドレスの形式が正しくありません。";
message.style.color = "red";
nameInput.style.border = "1px solid #ccc";
emailInput.style.border = "2px solid red";
return;
}
const profile = { name, email };
localStorage.setItem("profile", JSON.stringify(profile));
message.textContent = "プロフィールを保存しました。";
message.style.color = "black";
nameInput.style.border = "1px solid #ccc";
emailInput.style.border = "1px solid #ccc";
});
</script>
</body>
</html>
動きはしますが、
読みやすさ・変更しやすさ・再利用性のどれも微妙です。
ここから「関数分割の視点」で分解していきます。
どこを関数に分けるべきかを見極める
「役割のかたまり」を探す
このコードには、明らかに役割が分かれている部分があります。
保存済みプロフィールの読み込み
入力値の取得
入力チェック(バリデーション)
エラー表示(メッセージ+スタイル)
成功時の表示(メッセージ+スタイル)
localStorage への保存
これらを、順番に関数として切り出していきます。
ポイントは「一気に完璧を目指さない」ことです。
まずは分けやすいところから分けていきます。
ステップ1:バリデーションを関数にする
validateProfile 関数を作る
名前とメールのチェックを、1つの関数にまとめます。
function validateProfile(name, email) {
if (name === "") {
return {
field: "name",
message: "名前を入力してください。"
};
}
if (email === "") {
return {
field: "email",
message: "メールアドレスを入力してください。"
};
}
if (!email.includes("@")) {
return {
field: "email",
message: "メールアドレスの形式が正しくありません。"
};
}
return null;
}
JavaScriptここでのポイントは、
エラーがあれば「どの項目か」と「メッセージ」を返す
エラーがなければ null を返す
という形にしていることです。
こうしておくと、後で「どの input を赤くするか」を判断しやすくなります。
イベントハンドラからチェック処理を追い出す
saveButton.addEventListener("click", () => {
const name = nameInput.value.trim();
const email = emailInput.value.trim();
const error = validateProfile(name, email);
if (error !== null) {
// ここでエラー表示を行う(後で関数にする)
return;
}
const profile = { name, email };
localStorage.setItem("profile", JSON.stringify(profile));
// ここで成功表示を行う(後で関数にする)
});
JavaScriptこれだけでも、だいぶ「流れ」が見やすくなりました。
ステップ2:表示処理を関数にする
エラー表示用の関数
function showError(error) {
message.textContent = error.message;
message.style.color = "red";
nameInput.style.border = "1px solid #ccc";
emailInput.style.border = "1px solid #ccc";
if (error.field === "name") {
nameInput.style.border = "2px solid red";
}
if (error.field === "email") {
emailInput.style.border = "2px solid red";
}
}
JavaScriptここでは、
一度すべての枠線をリセットする
エラーの対象フィールドだけ赤くする
という流れを関数の中に閉じ込めています。
成功表示用の関数
function showSuccess() {
message.textContent = "プロフィールを保存しました。";
message.style.color = "black";
nameInput.style.border = "1px solid #ccc";
emailInput.style.border = "1px solid #ccc";
}
JavaScriptこれで、イベントハンドラはさらにシンプルにできます。
saveButton.addEventListener("click", () => {
const name = nameInput.value.trim();
const email = emailInput.value.trim();
const error = validateProfile(name, email);
if (error !== null) {
showError(error);
return;
}
const profile = { name, email };
localStorage.setItem("profile", JSON.stringify(profile));
showSuccess();
});
JavaScript「何をしているか」が、ほぼ日本語の文章のように読めるはずです。
ステップ3:読み込み処理も関数にする
loadProfile 関数を作る
ページ読み込み時の処理も、関数にしておきます。
function loadProfile() {
const savedProfileText = localStorage.getItem("profile");
if (savedProfileText === null) {
return;
}
const savedProfile = JSON.parse(savedProfileText);
nameInput.value = savedProfile.name;
emailInput.value = savedProfile.email;
}
JavaScriptこれをスクリプトの初期化部分で呼び出します。
loadProfile();
JavaScriptこうしておくと、
「初期化処理はここ」「イベント処理はここ」「バリデーションはここ」
というふうに、コードの役割がはっきり分かれます。
関数同士の関係を意識する
「上から読んで分かる構造」にする
ここまで分割したコードを、関係性で整理してみます。
loadProfile
初期化時に一度だけ呼ばれる
localStorage からデータを読み込んで input に反映する
validateProfile
イベントハンドラから呼ばれる
入力値をチェックして、エラー情報か null を返す
showError
イベントハンドラから呼ばれる
エラー情報をもとにメッセージとスタイルを更新する
showSuccess
イベントハンドラから呼ばれる
成功メッセージとスタイルを更新する
イベントハンドラ(saveButton の click)
入力値を取得する
validateProfile を呼ぶ
エラーなら showError
成功なら localStorage に保存して showSuccess
このように、「誰が誰を呼ぶか」が整理されていると、
コード全体の見通しが一気によくなります。
どこまで分けるかの判断基準
「同じことを2回書きそうなら分ける」
関数分割の一つの目安は、
同じようなコードをコピペしそうになったら、関数にする
です。
例えば、エラー表示のスタイルを変えるコードを、
あちこちにコピペして書き始めたら危険信号です。
showError のような関数にまとめてしまえば、
「見た目を変えたいときはここだけ触ればいい」状態になります。
「名前を付けられるなら分ける」
もう一つの目安は、
この処理に名前を付けるとしたら、何と呼ぶか?
と自分に問いかけてみることです。
「プロフィールを読み込む処理」→ loadProfile
「プロフィールを保存する処理」→ saveProfile
「プロフィールをチェックする処理」→ validateProfile
のように、自然な名前が浮かぶなら、
それは関数として独立させる価値が高い処理です。
セキュリティの視点から見た関数分割
「危険な処理を一箇所に閉じ込める」
セキュリティの観点でも、関数分割はとても重要です。
例えば、
ユーザー入力を画面に表示する
localStorage に保存する
サーバーに送信する
といった「危険度の高い処理」は、
専用の関数に閉じ込めておくと安全です。
画面表示には必ず textContent を使う関数を用意する
localStorage に保存する前に、必ずバリデーション関数を通す
といったルールを「関数レベル」で決めておくと、
うっかり innerHTML を使って XSS の穴を開ける、
といった事故を防ぎやすくなります。
関数分割は、単に「きれいに書くテクニック」ではなく、
安全なコードを書くための土台にもなります。
Day21 後半のミニ完成コード
関数分割後の全体像
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Day21 関数分割 後半</title>
<style>
input {
border: 1px solid #ccc;
padding: 4px;
}
</style>
</head>
<body>
<h1>プロフィール設定(関数分割版)</h1>
<p>名前</p>
<input id="nameInput" type="text">
<p>メールアドレス</p>
<input id="emailInput" type="text">
<p>
<button id="saveButton">保存</button>
</p>
<p id="message"></p>
<script>
const nameInput = document.getElementById("nameInput");
const emailInput = document.getElementById("emailInput");
const saveButton = document.getElementById("saveButton");
const message = document.getElementById("message");
function loadProfile() {
const savedProfileText = localStorage.getItem("profile");
if (savedProfileText === null) {
return;
}
const savedProfile = JSON.parse(savedProfileText);
nameInput.value = savedProfile.name;
emailInput.value = savedProfile.email;
}
function validateProfile(name, email) {
if (name === "") {
return { field: "name", message: "名前を入力してください。" };
}
if (email === "") {
return { field: "email", message: "メールアドレスを入力してください。" };
}
if (!email.includes("@")) {
return { field: "email", message: "メールアドレスの形式が正しくありません。" };
}
return null;
}
function showError(error) {
message.textContent = error.message;
message.style.color = "red";
nameInput.style.border = "1px solid #ccc";
emailInput.style.border = "1px solid #ccc";
if (error.field === "name") {
nameInput.style.border = "2px solid red";
}
if (error.field === "email") {
emailInput.style.border = "2px solid red";
}
}
function showSuccess() {
message.textContent = "プロフィールを保存しました。";
message.style.color = "black";
nameInput.style.border = "1px solid #ccc";
emailInput.style.border = "1px solid #ccc";
}
function saveProfile(name, email) {
const profile = { name, email };
localStorage.setItem("profile", JSON.stringify(profile));
}
saveButton.addEventListener("click", () => {
const name = nameInput.value.trim();
const email = emailInput.value.trim();
const error = validateProfile(name, email);
if (error !== null) {
showError(error);
return;
}
saveProfile(name, email);
showSuccess();
});
loadProfile();
</script>
</body>
</html>
このコードは、行数だけ見れば「長くなった」ように見えます。
でも、読むとき・直すとき・拡張するときのストレスは、
最初のベタ書き版とは比べものになりません。
Day21 後半のまとめ
後半では、実際の小さなアプリを題材にして、
どこを関数に分けるかを見極める
バリデーション・表示・保存・読み込みを関数に分ける
関数同士の関係(誰が誰を呼ぶか)を意識する
「同じことを2回書きそうなら関数にする」という判断基準
セキュリティ的に重要な処理を関数に閉じ込める
という、大規模化の基礎を具体的に見てきました。
ここまでの感覚が身についていると、
これからコードが長くなっても「整理しながら書く」ことができます。
Day22 以降は、この関数分割の感覚を前提に、
さらに一段上の設計や機能に進んでいけます。
