Day 73:再利用可能な関数
Day 73では、何度も使える処理を小さな関数としてまとめる方法を学びます。今回のテーマは、Utility関数と共通処理です。実践では、日付フォーマット、文字列処理、数値フォーマットを例に、入力を受け取り、整えた結果を返す関数を作ります。
Day 72では、API処理・データ処理・UI処理を役割ごとに分けました。Day 73では、そのうち複数の場面から使える処理を取り出し、再利用しやすい形にします。たとえば、日付を画面に表示する場所が複数あれば、毎回同じ書式変換を書くのではなく、共通の関数を使えます。こうすると、同じ処理を何度も書く手間が減り、表示ルールを変えるときも修正箇所をまとめられます。
Utility関数とは
Utility関数とは、日付や文字列、数値などに対する、汎用的で小さな処理をまとめた関数です。アプリの特定画面だけに依存せず、複数の場所から呼び出せるように作ります。
たとえば、次のような関数がUtility関数に向いています。
- 日付を「2026年11月4日」のような形に整える
- 文字列の前後の空白を取り除く
- 数値を桁区切りで表示する
- 金額に通貨記号を付けて表示する
関数にする基本形は、「入力を受け取り、決まった処理を行い、結果を返す」です。画面の要素を直接変更するのではなく、値を返すようにすると、画面表示やテストから独立して使いやすくなります。
なぜ共通処理を関数にするのか
同じ処理を複数の場所にコピーすると、あとからルールを変えたときに、すべてのコピーを探して直さなければなりません。修正漏れがあると、画面によって書式が異なることもあります。
一方、共通処理を関数にまとめれば、呼び出す場所は同じ関数を使うだけです。たとえば日付の表示ルールを変更するときは、その関数を直せば、利用している場所へ同じルールを適用できます。
ただし、似ている処理を何でも無理に共通化する必要はありません。役割やルールが本当に同じ処理をまとめることが大切です。
最初に決めること:関数の約束
関数を作る前に、入力と出力の約束を決めます。たとえば、日付フォーマット関数なら、「どの形式の値を受け取るか」「不正な値ならどうするか」「何を返すか」を決めます。
- 入力:JavaScriptの`Date`オブジェクト、または日付として解釈できる値
- 出力:指定した表示形式の日付文字列
- 不正な入力:エラーを投げる、または決めた代替表示を返す
この約束が曖昧なままだと、呼び出す側が関数の扱いに迷います。初心者の方は、関数を作るときに「何を受け取り、何を返すか」を先に言葉で説明する習慣をつけるとよいです。
ステップ1:文字列を整えるUtility関数
まず、文字列の前後にある空白を取り除く関数を作ります。検索欄やフォーム入力の処理でよく使う共通処理です。
// 文字列の前後の空白を取り除いて返す
export function trimText(value) {
// 文字列以外を誤って受け取った場合は、扱い方を明確にする
if (typeof value !== "string") {
throw new TypeError("trimTextには文字列を渡してください。");
}
return value.trim();
}JavaScript使うときは次のようにします。
import { trimText } from "./utils.js";
const keyword = trimText(" JavaScript ");
console.log(keyword); // "JavaScript"JavaScriptここでは、trim()を呼び出すだけの小さな関数にしています。アプリ内で同じ入力ルールを使いたいとき、関数名で意図を示せるのが利点です。ただし、単に一度だけ使う短い処理なら、関数にする必要はありません。複数の場所で使う、またはルールに名前を付けたい場合に関数化を検討します。
文字列の表記ゆれも整える
検索で大文字・小文字を区別したくないときは、文字列を同じ形にそろえてから比較できます。
// 検索用に文字列の前後の空白を取り、大文字小文字をそろえる
export function normalizeSearchText(value) {
if (typeof value !== "string") {
throw new TypeError("normalizeSearchTextには文字列を渡してください。");
}
return value.trim().toLowerCase();
}JavaScriptたとえば、検索語がJavaScriptでもjavascriptでも、小文字にそろえることで比較しやすくなります。
import { normalizeSearchText } from "./utils.js";
const keyword = normalizeSearchText(" JavaScript ");
const title = normalizeSearchText("JavaScript 入門");
// 両方を同じルールで整えてから検索する
console.log(title.includes(keyword)); // trueJavaScript文字列をどこまで整えるかは、アプリの目的によって異なります。日本語検索などでは、英字の大文字・小文字だけをそろえれば十分な場合もあります。入力を必要以上に変換すると、本来区別したい文字まで変わることがあるため、目的に合わせてルールを決めましょう。
ステップ2:日付をフォーマットする
JavaScriptのDateオブジェクトは日付や時刻を扱えますが、そのまま表示すると読みづらい場合があります。Intl.DateTimeFormatを使うと、指定した地域やタイムゾーンに合わせた表示を作れます。
// 日付を日本語の年月日形式に整えて返す
export function formatDate(value) {
const date = value instanceof Date ? value : new Date(value);
// 無効な日付かどうかを確認する
if (Number.isNaN(date.getTime())) {
throw new TypeError("有効な日付を指定してください。");
}
// 日本語・日本時間の年月日として表示する
return new Intl.DateTimeFormat("ja-JP", {
year: "numeric",
month: "long",
day: "numeric",
timeZone: "Asia/Tokyo"
}).format(date);
}JavaScript使い方は次の通りです。
import { formatDate } from "./utils.js";
const publishedAt = new Date("2026-11-04T09:30:00+09:00");
console.log(formatDate(publishedAt));
// 例: 2026年11月4日JavaScript日付で注意したいのは、日付と時刻のタイムゾーンです。APIから届く日時がUTCなのか、日本時間なのかを確認しないまま表示すると、環境によって時刻や日付がずれて見える場合があります。日時を扱うときは、入力データの形式と表示したいタイムゾーンを決めておきましょう。
ステップ3:数値を読みやすく表示する
大きな数値は、桁区切りを入れると読みやすくなります。Intl.NumberFormatを使うと、地域に合わせた数値表示を作れます。
// 数値を日本語形式の桁区切りで表示する
export function formatNumber(value) {
// 数値以外やNaN、Infinityを誤って表示しないよう確認する
if (typeof value !== "number" || !Number.isFinite(value)) {
throw new TypeError("formatNumberには有限の数値を渡してください。");
}
return new Intl.NumberFormat("ja-JP").format(value);
}JavaScriptimport { formatNumber } from "./utils.js";
console.log(formatNumber(1234567)); // "1,234,567"JavaScript金額を表示する場合は、通貨形式を指定できます。
// 数値を日本円の通貨形式で表示する
export function formatYen(value) {
if (typeof value !== "number" || !Number.isFinite(value)) {
throw new TypeError("formatYenには有限の数値を渡してください。");
}
return new Intl.NumberFormat("ja-JP", {
style: "currency",
currency: "JPY"
}).format(value);
}JavaScriptimport { formatYen } from "./utils.js";
console.log(formatYen(2500)); // 例: ¥2,500JavaScriptIntl.NumberFormatを使うと、桁区切りや通貨記号の表示を自分で組み立てずに済みます。数値を文字列へ変換してから処理するのではなく、数値のまま渡すのが基本です。
ステップ4:Utility関数をファイルにまとめる
Day 71で学んだES Modulesを使い、共通関数をutils.jsへまとめます。たとえば、次のような構成にできます。
project/
├── index.html
└── js/
├── app.js
└── utils.js
js/utils.jsには、今回作った関数をまとめます。
// 文字列の前後の空白を取り除く
export function trimText(value) {
if (typeof value !== "string") {
throw new TypeError("trimTextには文字列を渡してください。");
}
return value.trim();
}
// 検索用に文字列を整える
export function normalizeSearchText(value) {
if (typeof value !== "string") {
throw new TypeError("normalizeSearchTextには文字列を渡してください。");
}
return value.trim().toLowerCase();
}
// 日付を日本語の年月日形式に整える
export function formatDate(value) {
const date = value instanceof Date ? value : new Date(value);
if (Number.isNaN(date.getTime())) {
throw new TypeError("有効な日付を指定してください。");
}
return new Intl.DateTimeFormat("ja-JP", {
year: "numeric",
month: "long",
day: "numeric",
timeZone: "Asia/Tokyo"
}).format(date);
}
// 数値を桁区切りで表示する
export function formatNumber(value) {
if (typeof value !== "number" || !Number.isFinite(value)) {
throw new TypeError("formatNumberには有限の数値を渡してください。");
}
return new Intl.NumberFormat("ja-JP").format(value);
}
// 数値を日本円として表示する
export function formatYen(value) {
if (typeof value !== "number" || !Number.isFinite(value)) {
throw new TypeError("formatYenには有限の数値を渡してください。");
}
return new Intl.NumberFormat("ja-JP", {
style: "currency",
currency: "JPY"
}).format(value);
}JavaScriptステップ5:別のファイルから利用する
app.jsから必要な関数を読み込みます。使う関数だけをimportすると、何に依存しているかがわかりやすくなります。
// utils.jsから必要な関数を読み込む
import {
formatDate,
formatNumber,
formatYen,
normalizeSearchText,
trimText
} from "./utils.js";
// 入力文字列を整える
const keyword = trimText(" JavaScript ");
// 検索用の文字列に変換する
const normalizedKeyword = normalizeSearchText(keyword);
// 日付・数値・金額を整形する
const dateText = formatDate("2026-11-04T09:30:00+09:00");
const countText = formatNumber(1234567);
const priceText = formatYen(2500);
// 整形した値を確認する
console.log(normalizedKeyword);
console.log(dateText);
console.log(countText);
console.log(priceText);JavaScriptブラウザーで使う場合は、HTMLからapp.jsをモジュールとして読み込みます。
<!-- ES Modulesとしてapp.jsを読み込む -->
<script type="module" src="./js/app.js"></script>
再利用可能な関数にするためのポイント
関数を再利用しやすくするには、いくつかの基本があります。
- 関数名から処理の内容が想像できるようにします。
- 入力と戻り値を明確にします。
- できるだけ一つの目的に絞ります。
- 引数で必要な値を受け取り、外部の変数に過度に依存しないようにします。
- 不正な入力を受け取った場合の動きを決めます。
特に、関数の中でHTML要素を直接探したり、画面の状態を変更したりしないUtility関数は、別の画面や別のアプリでも使いやすくなります。たとえば、formatDate(date)が文字列を返す形なら、カード表示でもテーブル表示でも同じ関数を利用できます。
テストしやすい関数にする
入力を受け取って結果を返す関数は、画面を開かなくても動作を確認しやすいです。まずはコンソールで、通常の値と境界となる値を試してみます。
import { formatNumber, formatYen, normalizeSearchText } from "./utils.js";
// 通常の値を確認する
console.log(formatNumber(1500));
console.log(formatYen(1500));
console.log(normalizeSearchText(" Hello "));
// 不正な入力ではエラーになることを確認する
try {
formatNumber("1500");
} catch (error) {
console.error(error.message);
}JavaScriptこの例のformatNumber()は数値だけを受け付け、文字列の"1500"は受け付けません。どの入力を許可するかは設計によって異なりますが、関数のルールを決めて一貫させることが大切です。
初心者が注意したい設計上のポイント
Utility関数は便利ですが、何でもutils.jsに詰め込むと、ファイルが大きくなって探しにくくなります。関数が増えてきたら、役割ごとにファイルを分ける方法もあります。
- `dateUtils.js`:日付に関する処理
- `stringUtils.js`:文字列に関する処理
- `numberUtils.js`:数値に関する処理
小さな学習アプリでは、まず一つのutils.jsで構いません。必要以上に細かく分けず、探しやすさや再利用のしやすさを見ながら整理します。
また、関数が「何でもする」状態にならないように注意します。たとえば「日付を整形し、DOMを書き換え、API通信もする関数」は、複数の役割が混ざっています。Day 72で学んだ役割分担を思い出し、Utility関数は値の変換や共通処理に集中させましょう。
情報セキュリティの観点
文字列を画面へ表示するときは、APIやユーザー入力の値をそのままHTMLとして解釈させないことが大切です。表示にはtextContentを使うのが基本です。Utility関数で文字列を整えたとしても、それだけで安全なHTMLになるわけではありません。文字列の整形と、画面への安全な表示は別の処理として考えます。
また、日付や数値の関数で不正な入力をどう扱うかを明確にしておくと、予期しない値による表示崩れを防ぎやすくなります。
Day 73の練習課題
基本の関数を動かしたら、次の課題に取り組んでみましょう。
- `formatDate()`に曜日を表示する設定を追加します。
- 文字列の連続した空白を1つにまとめる関数を作ります。
- 小数を指定した桁数に丸めて表示する関数を作ります。
- 日付や数値の不正な入力を渡し、エラーの動きを確認します。
- 作ったUtility関数を、別の画面表示処理から読み込んで使います。
追加機能を作るときは、まず「入力は何か」「返す値は何か」「失敗時はどうするか」を決めてからコードにしましょう。
Day 73のゴール
Day 73では、次のことができるようになっていれば十分です。
- Utility関数が共通処理をまとめるものだと説明できる
- 入力を受け取り、結果を返す関数を作れる
- 日付・文字列・数値の整形処理を関数化できる
- ES Modulesの`export`と`import`で関数を再利用できる
- 入力値や不正な値の扱いを関数ごとに決められる
まとめ
Day 73では、Utility関数と共通処理を学び、日付フォーマット、文字列処理、数値フォーマットを再利用可能な関数として作りました。大切なのは、同じ処理を何度もコピーするのではなく、入力と出力が明確な関数にまとめることです。さらに、ES Modulesを使って関数を別ファイルへ置けば、必要な場所から読み込んで利用できます。
ただし、関数化そのものが目的ではありません。何度も使う処理や、名前を付けてルールを統一したい処理を、無理のない単位でまとめることが重要です。今回の関数を実際に使い、入力と戻り値、不正な入力への対応を確認しながら、読みやすく再利用しやすいコードを書く習慣を身につけていきましょう。
