【JavaScript】数値を文字列として結合する方法|型変換の仕組み・テンプレートリテラル・toString・+ 演算子の落とし穴まで解説

JavaScript で「数値と文字列を結合する」操作は日常的に使いますが、+ 演算子の振る舞いによって計算になるか文字列結合になるかが変わります。この違いを理解していないと、計算結果が 1 + 2 = "12" になるバグを生み出すことがあります。

この記事では型変換の仕組みを正確に解説したうえで、テンプレートリテラル・String()toString()などの使い分け、実務でよく使う数値フォーマットパターンまで体系的にまとめます。

スポンサーリンク

+ 演算子の型変換の仕組み

+ 演算子は左辺・右辺のどちらかに 文字列が含まれると文字列結合になります。どちらも数値なら算術加算です。

+ 演算子の動作
// 数値 + 数値 → 算術加算
console.log(1 + 2);          // 3

// 数値 + 文字列 → 文字列結合(左から評価)
console.log(1 + '2');        // '12'
console.log('1' + 2);        // '12'
console.log('価格: ' + 100); // '価格: 100'

// 複数の + が絡む場合は左から順に評価される
console.log(1 + 2 + '円');   // '3円'  (1+2=3 → '3'+'円')
console.log('合計: ' + 1 + 2); // '合計: 12'  ('合計: '+1='合計: 1' → '合計: 1'+'2')

// 意図通りにするには計算部分を括弧でまとめる
console.log('合計: ' + (1 + 2)); // '合計: 3'
'合計: ' + 1 + 2'合計: 12' になる:+ は左から右へ評価されるため、'合計: ' + 1 が先に '合計: 1'(文字列)になり、その後 '合計: 1' + 2'合計: 12' になります。数値の計算が混在する場合は ( ) で優先順位を明示してください。

数値を文字列に変換する方法の比較

数値を文字列に変換する方法は複数あります。用途に合わせて使い分けましょう。

方法 結果 特徴
String(n) String(42) "42" null/undefined でも安全(エラーにならない)
n.toString() (42).toString() "42" 基数(2進数・16進数等)を指定できる
n.toString(2) (255).toString(2) "11111111" 2進数に変換
n.toString(16) (255).toString(16) "ff" 16進数に変換(カラーコードなど)
n.toFixed(桁) (3.14159).toFixed(2) "3.14" 小数点以下の桁数を固定(返り値は文字列)
n.toLocaleString() (1000000).toLocaleString("ja-JP") "1,000,000" 3桁区切り・通貨記号などロケール対応
テンプレートリテラル `値: ${42}` "値: 42" 埋め込みが最も読みやすい
"" + n "" + 42 "42" 暗黙変換・非推奨(可読性が低い)
各変換方法の使い方
const n = 255;

// 基本変換
console.log(String(n));        // '255'
console.log(n.toString());     // '255'

// 基数変換
console.log(n.toString(2));    // '11111111'  (2進数)
console.log(n.toString(8));    // '377'       (8進数)
console.log(n.toString(16));   // 'ff'        (16進数)

// 小数点桁数固定
console.log((3.14159).toFixed(2)); // '3.14'  ← 返り値は文字列!
console.log((1.005).toFixed(2));   // '1.00' や '1.01'(浮動小数点の誤差に注意)

// ロケール対応
console.log((1234567).toLocaleString('ja-JP'));           // '1,234,567'
console.log((9800).toLocaleString('ja-JP', { style: 'currency', currency: 'JPY' })); // '¥9,800'

テンプレートリテラルによる文字列結合(推奨)

ES2015 以降はバッククォート(`)を使ったテンプレートリテラルが最も読みやすく推奨される書き方です。${ } 内は式として評価されるため、数値の計算もそのまま書けます。

テンプレートリテラルの使い方
const price  = 1980;
const count  = 3;
const name   = '商品A';

// 基本的な埋め込み
console.log(`${name}は${price}円です`);            // '商品Aは1980円です'

// ${ } 内で計算できる
console.log(`合計: ${price * count}円`);            // '合計: 5940円'

// メソッド呼び出しも可能
console.log(`合計: ${(price * count).toLocaleString('ja-JP')}円`); // '合計: 5,940円'

// 複数行の文字列
const receipt = `
商品: ${name}
単価: ${price.toLocaleString('ja-JP')}円
数量: ${count}
合計: ${(price * count).toLocaleString('ja-JP')}円
`.trim();
console.log(receipt);
テンプレートリテラルは + 結合より読みやすい:'商品名: ' + name + ' は ' + price + '円です' より`商品名: ${name} は ${price}円です` のほうが圧倒的に読みやすく、演算順序のバグも起きません。新規コードではテンプレートリテラルを優先してください。

配列の数値を文字列として結合する

Array.prototype.join() を使うと、配列の各要素を区切り文字で結合した文字列を作れます。

join() で配列要素を結合
const nums = [1, 2, 3, 4, 5];

console.log(nums.join(''));      // '12345'(区切りなし)
console.log(nums.join(', '));    // '1, 2, 3, 4, 5'
console.log(nums.join(' - '));   // '1 - 2 - 3 - 4 - 5'

// 電話番号の組み立て
const tel = [90, 1234, 5678];
console.log(tel.join('-'));      // '90-1234-5678'

// reduce での結合(join で代替できる場合は join を使う)
const result = nums.reduce((acc, n) => acc + String(n), '');
console.log(result);             // '12345'

実務でよく使う数値フォーマットパターン

価格・ゼロ埋め・単位付き表示
// ─── 価格表示(3桁区切り + 円マーク)───
function formatPrice(n) {
  return `¥${n.toLocaleString('ja-JP')}`;
}
console.log(formatPrice(9800));       // '¥9,800'
console.log(formatPrice(1234567));    // '¥1,234,567'

// ─── ゼロ埋め(padStart)───
// n桁になるよう先頭に '0' を補完する
const num = 5;
console.log(String(num).padStart(3, '0'));   // '005'
console.log(String(num).padStart(5, '0'));   // '00005'

// 時刻の表示(2桁ゼロ埋め)
function fmt2(n) { return String(n).padStart(2, '0'); }
const h = 9, m = 5, s = 3;
console.log(`${fmt2(h)}:${fmt2(m)}:${fmt2(s)}`); // '09:05:03'

// ─── 小数点桁数を揃えた表示 ───
console.log((1.5).toFixed(2));       // '1.50'
console.log((100).toFixed(2));       // '100.00'

// ─── 単位付き表示 ───
function formatBytes(bytes) {
  if (bytes < 1024)       return `${bytes} B`;
  if (bytes < 1024 ** 2)  return `${(bytes / 1024).toFixed(1)} KB`;
  if (bytes < 1024 ** 3)  return `${(bytes / 1024 ** 2).toFixed(1)} MB`;
  return `${(bytes / 1024 ** 3).toFixed(2)} GB`;
}
console.log(formatBytes(500));       // '500 B'
console.log(formatBytes(2048));      // '2.0 KB'
console.log(formatBytes(1572864));   // '1.5 MB'

Intl.NumberFormat で高度な数値フォーマット

繰り返し使う場合や多言語対応が必要なケースでは、Intl.NumberFormat を使うと効率的です。

Intl.NumberFormat の使い方
// ─── 通貨表示 ───
const jpyFmt = new Intl.NumberFormat('ja-JP', {
  style: 'currency', currency: 'JPY'
});
console.log(jpyFmt.format(9800));     // '¥9,800'
console.log(jpyFmt.format(1234567));  // '¥1,234,567'

// ─── 小数点桁数の固定 ───
const pctFmt = new Intl.NumberFormat('ja-JP', {
  style: 'percent',
  minimumFractionDigits: 1,
  maximumFractionDigits: 1,
});
console.log(pctFmt.format(0.1234));   // '12.3%'

// ─── 単位付き(m・kg など)───
const kgFmt = new Intl.NumberFormat('ja-JP', {
  style: 'unit', unit: 'kilogram'
});
console.log(kgFmt.format(72.5));      // '72.5 kg'
toLocaleString() と Intl.NumberFormat の使い分け:n.toLocaleString() は手軽ですが、同じフォーマッターを繰り返し使う場合はnew Intl.NumberFormat() インスタンスを使い回すほうがパフォーマンス上有利です。ループ内で多数の数値をフォーマットする場合は Intl.NumberFormat を推奨します。

toFixed() の浮動小数点の落とし穴

toFixed の誤差
// JavaScript の浮動小数点演算(IEEE 754)による誤差
console.log((1.005).toFixed(2));  // '1.00' または '1.01'(環境依存)
console.log((1.255).toFixed(2));  // '1.25' または '1.26'

// 金額計算で正確さが必要な場合の対処法
// ① 整数に変換して計算してから最後に割り算
function toFixed2(n) {
  return (Math.round(n * 100) / 100).toFixed(2);
}
console.log(toFixed2(1.005));     // '1.01'(丸め誤差を軽減)

// ② 金融計算ライブラリ(decimal.js など)を使う方法もある
金融・税計算に toFixed() を直接使わない:(1.005).toFixed(2)"1.00" になるのは、2進数で表現できない小数の誤差によるものです。金額の計算には Math.round(n * 100) / 100 で丸めてから toFixed() を使うか、decimal.js などのライブラリを検討してください。

よくある質問

QString(null) と null.toString() の違いは何ですか?
AString(null)"null" という文字列を返します。null.toString()TypeError: Cannot read properties of null でクラッシュします。変数が nullundefined になる可能性がある場合は必ず String() を使ってください。
Qテンプレートリテラルで undefined や null を埋め込むとどうなりますか?
Aundefined は文字列 "undefined"null"null" として埋め込まれます。ユーザーに見せる文字列でこれらが表示されると不自然なので、${value ?? "未設定"}${value || "—"} のようにデフォルト値を設定してください。
Q数値の文字列結合と + による加算を混在させないためのベストプラクティスは?
A数値計算は先に完了させてから文字列に変換するのが基本原則です。計算部分を ( ) でまとめるか、計算結果を変数に入れてからテンプレートリテラルで使う方法が最も安全です。const total = price * count; console.log(`合計: ${total}円`); のようなパターンを習慣にしてください。
Q数値を16進数文字列に変換して、6桁(カラーコード形式)にするには?
An.toString(16).padStart(6, "0").toUpperCase() のようにします。例えば (255).toString(16)"ff" ですが、"ff".padStart(6, "0")"0000ff" になります。# を付けるには先頭に連結します: "#" + n.toString(16).padStart(6, "0")
QpadStart は古いブラウザでも使えますか?
AString.prototype.padStart() は ES2017 で追加されたメソッドです。IE11 は対応していません。IE11 をサポートする必要がある場合は、("000" + n).slice(-3) のような slice を使ったゼロ埋めでポリフィルできます。現代の開発では Babel や core-js によるポリフィルを使うか、IE11 非対応と割り切ることが一般的です。

まとめ

数値と文字列の結合・変換のポイントをまとめます。

  • + 演算子は左辺・右辺の一方が文字列だと文字列結合になる。複数の + は左から評価される
  • 数値を埋め込む文字列はテンプレートリテラル(バッククォート)が最も読みやすく安全
  • null/undefined になる可能性がある変数は n.toString() ではなく String(n) を使う
  • 3桁区切りや通貨表示は toLocaleString() または Intl.NumberFormat を使う
  • ゼロ埋めは String(n).padStart(桁数, "0") が簡潔
  • toFixed() は浮動小数点の誤差に注意。金融計算には Math.round との組み合わせかライブラリを使う

文字列の置換・操作については【JavaScript】replace()で文字列を置換する方法、四則演算の詳細は【JavaScript】四則演算と計算の基本もあわせてご覧ください。