JavaScript | ES6+ 文法:テンプレート文字列 – 関数呼び出し埋め込み

JavaScript
スポンサーリンク

関数呼び出し埋め込みとは何か

テンプレート文字列はバッククォートで囲み、${ ... } の中に“任意の式”を置けます。その式に関数呼び出しを書けば、戻り値が文字列の中にそのまま埋め込まれます。ここが重要です:変数だけでなく、計算・整形・サニタイズなどを関数で表現し、テンプレートには“見た目”だけを残すと読みやすく保守しやすいコードになります。

const name = "Alice";
const upper = s => s.toUpperCase();
const msg = `Hello, ${upper(name)}!`;
console.log(msg); // Hello, ALICE!
JavaScript

基本構文と文字列化のルール

任意の関数を呼び出して結果を挿入できる

${ ... } の中では通常の JavaScript と同じく、関数呼び出し・メソッドチェーン・三項演算子などが使えます。戻り値は自動的に文字列化されて結合されます。

const price = 123456;
const fmt = n => n.toLocaleString("ja-JP");
const text = `合計: ${fmt(price)} 円`;
JavaScript

オブジェクトはそのままでは [object Object]

関数がオブジェクトを返す場合、暗黙に文字列化されると [object Object] になります。ここが重要です:必要に応じて JSON 化や独自フォーマットで“文字列”にしてから埋め込みます。

const getUser = () => ({ id: 7, name: "Alice" });
const s1 = `user=${getUser()}`;                   // user=[object Object]
const s2 = `user=${JSON.stringify(getUser())}`;   // user={"id":7,"name":"Alice"}
JavaScript

整形を関数に分離する(見た目とロジックの分離)

テンプレート内に複雑な処理を書きすぎると読みにくくなります。ここが重要です:整形や計算は“補助関数”へ切り出し、テンプレートは最小限の埋め込みにするのが定石です。

const pad2 = n => String(n).padStart(2, "0");
const formatTime = d => `${pad2(d.getHours())}:${pad2(d.getMinutes())}:${pad2(d.getSeconds())}`;
const sanitizeHtml = s => String(s).replace(/[&<>"']/g, c => ({ 
  "&": "&", "<": "<", ">": ">", '"': """, "'": "'" 
}[c]));

const now = new Date();
const userInput = `<Alice>`;
const html = `
<div class="card">
  <h1>現在時刻: ${formatTime(now)}</h1>
  <p>こんにちは、${sanitizeHtml(userInput)} さん。</p>
</div>
`;
JavaScript

非同期関数の結果を埋め込む(async/await と組み合わせ)

テンプレート内でも await は“式”なので使えます。ただし、await は async 関数(またはトップレベル await が許される環境)内でのみ有効です。ここが重要です:非同期処理の結果を埋め込むときは、テンプレートを組み立てる関数自体を async にします。

// async 関数内ならテンプレート内で await が使える
const fetchJson = async url => (await fetch(url)).json();

async function page() {
  const data = await fetchJson("/api/item/1");
  return `
  <h1>${data.title}</h1>
  <p>価格: ${(data.price).toLocaleString("ja-JP")} 円</p>
  `;
}
JavaScript

パフォーマンスと副作用(重要ポイントの深掘り)

関数呼び出しは毎回評価されます。ここが重要です:重い処理や副作用(ログ、DOM 操作など)を ${ ... } の中で直接行うのは避け、事前計算した値を埋め込むほうが安全で速いです。ループ内でテンプレートを量産する場合は、共有の整形関数や定数を外に出して再利用します。

// 悪い例:テンプレート内で重い処理を直接
const bad = name => `結果: ${slowCompute(name)}`;

// 良い例:外で事前に計算してから埋め込む
const good = name => {
  const result = slowCompute(name);
  return `結果: ${result}`;
};
JavaScript

また、例外を投げる関数をテンプレート内で呼ぶと、その場でテンプレートの評価が失敗します。必要なら try/catch で値を用意してから埋め込みます。

const safeLine = user => {
  let label;
  try {
    label = makeLabel(user); // 例外が出る可能性あり
  } catch {
    label = "(不明)";
  }
  return `ユーザー: ${label}`;
};
JavaScript

実用例で理解を固める

// 1) テーブル行を生成:整形関数を活用
const yen = n => `${n.toLocaleString("ja-JP")} 円`;
const rowHtml = ({ id, price, active }) => `
<tr class="${active ? "on" : "off"}">
  <td>${id}</td>
  <td>${yen(price)}</td>
</tr>
`;
console.log(rowHtml({ id: 1, price: 123456, active: true }));

// 2) クエリ付き URL:エンコード関数を埋め込み
const q = "JS 入門 & 実践";
const url = base => `${base}?q=${encodeURIComponent(q)}&page=${1}`;
console.log(url("/search"));

// 3) メール本文:条件と関数呼び出しの併用
const upper = s => s.toUpperCase();
const planBadge = p => (p === "Pro" ? "★PRO" : "FREE");
const mail = ({ name, plan, note }) => `
こんにちは、${upper(name)} さん

ご利用中のプラン: ${planBadge(plan)}
${note ? `追記: ${note}\n` : ""}
よろしくお願いいたします。
`;
console.log(mail({ name: "Alice", plan: "Pro", note: "" }));

// 4) 非同期データでページ断片を組み立て
const fetchJson = async url => (await fetch(url)).json();
async function section() {
  const user = await fetchJson("/api/user/7");
  const money = (user.balance).toLocaleString("ja-JP");
  return `
  <section>
    <h2>${user.name}</h2>
    <p>残高: ${money} 円</p>
  </section>
  `;
}
JavaScript

まとめ

関数呼び出し埋め込みの核心は「${ ... } に関数を呼んで、戻り値を直接テンプレートへ挿入する」ことです。オブジェクトはそのままだと [object Object] になるため文字列化する、複雑な整形は補助関数へ分離する、重い処理は事前計算する、非同期はテンプレートを組み立てる関数ごと async にする。この指針を守れば、初心者でも見た目とロジックを美しく分離しながら、実用的で安全なテンプレート文字列を使いこなせます。

タイトルとURLをコピーしました