【実務・中級編】 String.prototype.padStart() – JavaScript実践ガイド

やあ、お疲れ。今日も元気にコード書いてるかい?
先日のプルリクエストで、日付のフォーマット処理にわざわざ三項演算子と文字列結合をネストさせているコードを見かけたんだ。気持ちは痛いほど分かる。「1桁の月や日に `0` をパディングしたい」という要件は、フロントエンド開発では息をするように出てくるからね。

でも、君も中級の壁を突破してシニアの領域に足を踏み入れようとしているフェーズだ。そろそろ、泥臭い自前の子守唄(ユーティリティ関数)を書くのは卒業して、JavaScriptが標準で用意してくれているエレガントな武器を使いこなそうぜ。

今回は、文字列操作の隠れた名脇役、`String.prototype.padStart()` について徹底的に解説する。実務でどう使うべきか、ブラウザの裏側の挙動も含めて叩き込むから、しっかりついてきてくれ。

—

1. なぜ今 `padStart()` なのか?仕様と基本のキ

`padStart()` は、その名の通り「文字列の先頭(Start)を指定した文字で埋めて(Pad)、特定の長さに達するようにする」メソッドだ。ES2017(ES8)で標準化されてから、もうすっかりモダンブラウザの常連になっている。

基本的な構文はこうだ。

str.padStart(targetLength, padString)

  • `targetLength`: 最終的に欲しい文字列の長さ。元の文字列の長さがこれより短い場合のみ、パディングが実行される。
  • `padString`: 埋める文字(省略した場合はデフォルトで半角スペース `” “` になる)。文字列が長すぎる場合は、途中で切り捨てられて指定長に調整される。

百聞は一見に如かず。まずは基本の動きを見てみよう。

// 5桁に満たない場合、先頭を ‘0’ で埋める(お馴染みのゼロ埋め)
const userId = “42”;
console.log(userId.padStart(5, “0”)); // 出力: “00042”

// 元の文字列がすでに指定長以上の場合は、そのまま返される(ここ重要!)
const longId = “123456”;
console.log(longId.padStart(5, “0”)); // 出力: “123456” (エラーにはならず、切り詰めもされない)

この「すでに長ければ何もしない」という安全設計が、実務においてどれだけバグを防いでくれるか、君ならすぐにピンとくるはずだ。

—

2. ブラウザは裏側でどう処理しているのか?(V8エンジン等の視点)

フロントエンド・スペシャリストとして、ただ「動く」だけではなく、JavaScriptエンジンが内部でどう動いているかも知っておいた方がいい。

ECMAScriptの仕様書(Spec)およびV8などのモダンなJavaScriptエンジンにおいて、`padStart`(およびその相棒の `padEnd`)が呼び出されると、大まかに以下のステップで処理が走る。

1. ToLength変換: 第一引数である `targetLength` を安全な整数に変換する。これが現在の文字列の長さ `S.length` 以下であれば、パディングの必要なしと判断され、元の文字列(正確にはそれを元にしたプリミティブな文字列)を即座に返す。
2. 長さの計算とメモリ確保: パディングすべき文字数(`targetLength – S.length`)を算出し、その分のメモリを内部的に確保する。
3. パディング文字列の生成: 第二引数の `padString` を繰り返して、必要な長分のパディング文字列を組み立てる。このとき、`padString` が空文字 `””` だった場合は、無限ループに陥らないようにパディングを行わず元の文字列を返す最適化が入る。
4. 連結(Concatenation): 生成したパディング文字列と元の文字列 `S` を結合して新しい文字列を返す。

ここで重要なのは、元の文字列自体はイミュータブル(変更不可能なプリ値)であり、常に新しい文字列がメモリ上に生成されるという点だ。
数万件の巨大な配列をループさせて毎フレーム `padStart` を呼び出すようなウルトラヘビーな処理を書く場合は、メモリ消費とガーベッジコレクション(GC)の負荷に少しだけ気を配る必要がある。とはいえ、UIの表示フォーマット程度であれば、パフォーマンス上のボトルネックになることはまずない。安心して使っていい。

—

3. 現場で使える!コピペOKの実用パターン集

さて、理論はこの辺にして、現場のコードでどう活かすかを見せよう。

パターンA:日付・時刻のゼロ埋め(定番)

バックエンドから送られてきた日時データを、ダッシュボードのUI用に整形するお決のパターンだ。

/

  • 数字を2桁の文字列にフォーマットする(例: 9 -> “09”)
  • @param {number|string} num
  • @returns {string}

/
const formatTwoDigits = (num) => {
return String(num).padStart(2, “0”);
};

const hours = 9;
const minutes = 5;

console.log(`${formatTwoDigits(hours)}:${formatTwoDigits(minutes)}`);
// 出力: “09:05” (美しく揃う!)

パターンB:クレジットカードや口座番号のマスク処理

セキュリティ要件でよくある、下4桁以外をマスク(隠蔽)する処理だ。`slice()` と組み合わせると非常にスマートに書ける。

/

  • カード番号の下4桁以外をマスクする
  • @param {string} cardNumber
  • @returns {string}

/
const maskCardNumber = (cardNumber) => {
// 下4桁を抽出
const lastFourDigits = cardNumber.slice(-4);
// 全体を16桁と仮定して、先頭から ” でパディングする
return lastFourDigits.padStart(16, “”);
};

const rawCard = “1234567890123456”;
console.log(maskCardNumber(rawCard));
// 出力: “3456″

どうだい? `if` 文や複雑なループを書かなくても、メソッドチェーンでこれだけスッキリ書ける。これがモダンJSの醍醐味さ。

パターンC:日本語や全角文字混じりの注意点(文字数カウントの罠)

ここで一つ、シニアとして君に実務の罠をシェアしておこう。
`padStart` はあくまで「文字単位(Code Point / 厳密にはUTF-16のコードユニット単位)」で長さをカウントする。

絵文字や一部の特殊な漢字(サロゲートペア)を扱うとき、JavaScriptの `length` は人間の見た目の文字数とズレることがある。

// サロゲートペアを含む文字列の場合
const emojiStr = “🔥”;
console.log(emojiStr.length); // 出力: 2 (UTF-16のコードユニットが2つ)

// これに対して padStart を使うと、予期せぬ崩れ方をすることがある
console.log(emojiStr.padStart(5, “0”));
// 出力: “000🔥” (文字数ベースではなくコードユニットベースで計算される)

通常のUIで扱う英数字や一般的な記号、日本語のひらがな・漢字(基本多言語面)であれば全く問題ないが、ユーザーが入力する自由記述のテキストや絵文字をパディングのターゲットにする場合は、文字数のカウントロジックにスプレッド構文 `[…str]` や `Intl.Segmenter` を使うべきかどうか、頭の片隅に置いておいてほしい。

—

4. まとめ:コードの意図を語れるエンジニアになろう

`padStart()` 自体は、MDNを3分読めば誰でも使えるようになる非常にシンプルなAPIだ。
しかし、「なぜこの場面でこれを選ぶのか」「どのようなエッジケース(文字数がすでに足りている場合や、予期せぬ入力値)をこのメソッドが内部でハンドリングしてくれているのか」を理解して使うのと、ただネットのコピペで貼るのとでは、エンジニアとしての厚みが全く変わってくる。

次に同じようなフォーマット処理を書くときは、自前の泥臭いユーティリティを捨てる勇気を持って、`padStart()` をスマートに差し込んでみてくれ。レビューするチームメイトも、「お、わかってるね」と唸るはずだ。

それじゃ、今日のところはここまで。また何か面白いJavaScriptの深淵に触れたくなったら、いつでも俺のところに聞きに来いよ。良いコードを!

コメント

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