10日目のゴールとテーマ
10日目のテーマは「同じことを何度も書かないための“関数化”と、コードの整理(リファクタリング)」です。
ここまでで、名簿アプリ・タスクアプリを通して、かなり“動くコード”を書いてきました。
今日は一段ギアを変えて、
「この書き方、ちょっとダサいな」を見つける目を持つ
同じパターンを“関数”として切り出す
読みやすく・直しやすいコードに整える
という、“プロ寄りの視点”を少しずつ身につけていきます。
まずは「今のタスクアプリの問題点」を言葉にしてみる
動くけど、ちょっとゴチャついてきていないか?
9日目までのタスクアプリを思い出してください。
タスク追加
完了切替
削除
一括削除
表示モード切り替え
件数カウント
localStorage 保存・読み込み
かなり機能が増えました。
その結果、main.js の中身はだいぶ長くなっているはずです。
例えば、こんな“モヤモヤ”が出てきていませんか?
同じような for 文が何回も出てくる
renderTasks の中でいろんなことをやりすぎている
イベントの中で直接ごちゃっと処理を書いていて、読み返すとつらい
この「なんかゴチャっとしてきたな」という感覚は、めちゃくちゃ大事です。
今日は、このモヤモヤを“きれいな形”に変えていきます。
関数化の基本的な考え方
「名前を付けて、ひとまとまりにする」
関数化の目的はシンプルです。
意味のあるひとまとまりに名前を付ける
同じ処理を何度も書かないようにする
「何をしているか」が一目で分かるようにする
例えば、次のようなコードがあったとします。
let undoneCount = 0;
for (let i = 0; i < tasks.length; i = i + 1) {
if (!tasks[i].done) {
undoneCount = undoneCount + 1;
}
}
JavaScriptこれがファイルのあちこちに3回出てきたら、
「これ、関数にした方がよくない?」と感じてほしいのです。
「未完了の数を数える」を関数にしてみる
同じロジックを1か所に集める
さっきの「未完了の数を数える」処理を、関数にしてみます。
function countUndoneTasks(tasks) {
let count = 0;
for (let i = 0; i < tasks.length; i = i + 1) {
if (!tasks[i].done) {
count = count + 1;
}
}
return count;
}
JavaScriptこの関数は、
tasks 配列を受け取る
未完了の数を数える
最後に return で結果を返す
という“ひとまとまりの意味”を持っています。
これを使うと、さっきの処理はこう書き換えられます。
let undoneCount = countUndoneTasks(tasks);
JavaScriptこれだけで、「あ、未完了の数を数えてるんだな」と一瞬で分かります。
中身の for 文を毎回読む必要がなくなります。
ここでの重要ポイントは、「関数名が“何をしているか”を説明してくれる」ということです。
関数化は、単にコードを短くするためではなく、「意図を見える化するため」にやります。
「状態が変わったらやること」を1か所にまとめる
render と save をセットで呼ぶのを忘れないようにする
9日目の時点では、タスクが変わるたびに、
renderTasks();
saveTasks();
JavaScriptのように、2行セットで書いていたはずです。
これがファイルのあちこちに出てくると、
どこかで片方だけ書き忘れる、という事故が起きやすくなります。
そこで、「状態が変わったときにやること」を1つの関数にまとめます。
function updateViewAndSave() {
renderTasks();
updateTaskCount();
saveTasks();
}
JavaScriptこうしておけば、タスクが変わる処理の最後は全部こう書けます。
updateViewAndSave();
JavaScriptこれで、
画面の一覧を更新する
件数表示を更新する
localStorage に保存する
という“3点セット”を、毎回確実に実行できます。
ここでの重要ポイントは、「よく一緒に呼ぶものは、まとめてしまう」という発想です。
これも立派なリファクタリングです。
イベントの中身を“薄くする”
「何が起きたか」と「どう処理するか」を分ける
今のコードでは、イベントリスナーの中に直接ロジックを書いている部分が多いはずです。
例えば、タスク追加はこんな感じでした。
addTaskButtonElement.addEventListener("click", function () {
let title = taskInputElement.value;
if (title === "") {
alert("タスクを入力してください。");
return;
}
let task = {
title: title,
done: false
};
tasks.push(task);
taskInputElement.value = "";
updateViewAndSave();
});
JavaScriptこれでも悪くはないのですが、
イベントの中に「入力チェック」「タスク作成」「配列更新」「画面更新」が全部詰まっています。
これを、「イベントは“きっかけ”だけ」「本体は別の関数」に分けてみます。
「タスクを追加する処理」を関数に切り出す
イベントからロジックを追い出す
まず、「タスクを追加する」という処理を関数にします。
function addTask(title) {
if (title === "") {
alert("タスクを入力してください。");
return;
}
let task = {
title: title,
done: false
};
tasks.push(task);
updateViewAndSave();
}
JavaScriptそして、イベント側はこう書き換えます。
addTaskButtonElement.addEventListener("click", function () {
let title = taskInputElement.value;
taskInputElement.value = "";
addTask(title);
});
JavaScriptこれで、イベントリスナーの役割は「入力欄から値を取り出して、関数に渡す」だけになりました。
ここでの重要ポイントは、「イベントは“何が起きたか”を受け取るだけ」「実際の処理は関数に任せる」という分離です。
こうしておくと、例えば「Enterキーでも追加したい」となったときに、
別のイベントからも addTask(title) を呼ぶだけで済みます。
「完了切替」と「削除」も関数にする
インデックスを渡せば動く“汎用関数”にする
同じように、「完了切替」と「削除」も関数にしてみます。
function toggleTaskDone(index) {
if (index < 0 || index >= tasks.length) {
return;
}
tasks[index].done = !tasks[index].done;
updateViewAndSave();
}
function deleteTask(index) {
if (index < 0 || index >= tasks.length) {
return;
}
tasks.splice(index, 1);
updateViewAndSave();
}
JavaScriptイベント委譲の部分は、こう書き換えられます。
taskListAreaElement.addEventListener("click", function (event) {
let target = event.target;
if (target.tagName !== "BUTTON") {
return;
}
let indexText = target.getAttribute("data-index");
let action = target.getAttribute("data-action");
if (indexText === null || action === null) {
return;
}
let index = Number(indexText);
if (Number.isNaN(index)) {
return;
}
if (action === "toggle") {
toggleTaskDone(index);
} else if (action === "delete") {
deleteTask(index);
}
});
JavaScriptイベント側は、「どのボタンが押されたかを判定して、適切な関数を呼ぶ」だけになりました。
実際のロジックは toggleTaskDone / deleteTask に閉じ込められています。
ここでの大事な感覚は、「関数にしておくと、テストもしやすいし、読みやすくもなる」ということです。
「配列をフィルタする処理」も関数にする
「未完了だけ」「完了だけ」などを共通化する
9日目で、「未完了だけを集める」「完了を除く」などの処理を何度か書きました。
これも関数にしておくと、コードがかなりスッキリします。
例えば、「未完了だけを集める」関数。
function filterUndoneTasks(tasks) {
let result = [];
for (let i = 0; i < tasks.length; i = i + 1) {
if (!tasks[i].done) {
result.push(tasks[i]);
}
}
return result;
}
JavaScript「完了を除いた新しい配列を作る」関数。
function removeDoneTasks(tasks) {
let result = [];
for (let i = 0; i < tasks.length; i = i + 1) {
if (!tasks[i].done) {
result.push(tasks[i]);
}
}
return result;
}
JavaScriptこれを使うと、一括削除はこう書けます。
function clearDoneTasks() {
tasks = removeDoneTasks(tasks);
updateViewAndSave();
}
JavaScriptイベント側はシンプルに。
clearDoneButtonElement.addEventListener("click", function () {
clearDoneTasks();
});
JavaScriptここでのポイントは、「配列を加工するロジックを1か所に集める」ことです。
もし「done の条件を変えたい」となったとき、
filterUndoneTasks / removeDoneTasks だけ直せば済みます。
「表示用のアイテムを作る処理」を関数にする
renderTasks の中身を少し分解する
今の renderTasks は、targetItems を作るところから HTML を組み立てるところまで、全部1つの関数に入っています。
これを少し分解してみます。
まず、「表示対象の items を作る」関数。
function buildTargetItems(tasks, filter) {
let items = [];
for (let i = 0; i < tasks.length; i = i + 1) {
let task = tasks[i];
if (filter === "undone" && task.done) {
continue;
}
items.push({
task: task,
index: i
});
}
return items;
}
JavaScript次に、「items から HTML を作る」関数。
function buildTasksHtml(items) {
if (items.length === 0) {
return "該当するタスクがありません。";
}
let html = "";
for (let i = 0; i < items.length; i = i + 1) {
let item = items[i];
let task = item.task;
let index = item.index;
let mark = task.done ? "✔" : "・";
let statusText = task.done ? "[完了]" : "[未完了]";
html = html +
'<div>' +
mark + " " + statusText + " " + task.title +
' <button data-index="' + index + '" data-action="toggle">完了切替</button>' +
' <button data-index="' + index + '" data-action="delete">削除</button>' +
'</div>';
}
return html;
}
JavaScriptそして、renderTasks はこうなります。
function renderTasks() {
let items = buildTargetItems(tasks, currentFilter);
let html = buildTasksHtml(items);
taskListAreaElement.innerHTML = html;
}
JavaScriptこれで、renderTasks 自体はかなり読みやすくなりました。
「表示対象を作る」
「HTMLを作る」
「画面に流し込む」
という三段階が、関数名だけで見えるようになっています。
10日目で一番大事な感覚
「動けばいい」から「読みやすく・直しやすく」へ
今日あなたがやったのは、
“新しい技術”というより、「今あるコードを一段きれいにする」という作業でした。
関数に名前を付けることで、「何をしているか」がコードから伝わるようになった。
イベントリスナーの中身を薄くして、「きっかけ」と「処理」を分けられるようになった。
配列を加工するロジックを関数に集めて、「直す場所」をはっきりさせられるようになった。
状態が変わったときにやること(render・カウント更新・保存)を1か所にまとめられた。
これはもう、「ただの初心者」から一歩抜け出して、
「他人が読んでも分かるコードを書こうとしている人」の領域です。
10日目のまとめ
今日のキーポイントを短く整理すると、こうなります。
同じ処理が複数回出てきたら「関数にできないか?」と考えるクセを持つ。
関数名は「何をしているか」が分かる日本語(ローマ字)にする。
イベントリスナーの中では、なるべく「値を集めて関数を呼ぶだけ」にする。
状態が変わったときに毎回やる処理(render・カウント・保存)を1つの関数にまとめる。
配列を加工する処理(フィルタ・削除など)を関数にして、ロジックを1か所に集める。
次の11日目では、
この「関数で整理されたコード」を前提にしながら、
「別ファイルに分ける」「コメントの書き方」「簡単なバグの見つけ方」など、
“現場で戦えるための基礎体力”をさらに積み上げていきます。
もし余裕があれば、
今日のタスクアプリのコードを見直して、「自分ならここも関数にするな」と思うところを1つ探してみてください。
その「自分なりの気持ち悪さ」を言語化して、直していくことが、
エンジニアとしてのセンスを一番育ててくれます。

