JavaScriptの「先頭・末尾判定」を極める:`startsWith`と`endsWith`の現場的使いこなし術
現場でコードレビューをしていると、今でもたまに見かけるんだ。「`str.indexOf(‘foo’) === 0`」なんて書き方。もちろん動くし、昔はそれが正攻法だった。でも、今の時代、そんなコードを残すのは「レガシーな香りを漂わせる」のと同じだ。
今日は、モダンJavaScriptにおける文字列の先頭・末尾判定の決定版、`startsWith`と`endsWith`について、現場の泥臭い実務に直結する話をしよう。
—
なぜ今さら`indexOf`や`slice`を卒業すべきなのか?
結論から言えば、「意図の明示」と「可読性」だ。
`str.indexOf(‘abc’) === 0` と書くと、読者は「おや、0番目にあることを確認しているのか?」と一瞬脳のメモリを消費する。一方で `str.startsWith(‘abc’)` とあれば、脳の処理は一瞬で終わる。「先頭がこれかを確認しているんだな」と。
コードはコンピュータに読ませるものだけど、メンテナンスするのは人間だ。この「意図の言語化」こそが、チーム開発における正義だよ。
基本の構文
使い方は非常にシンプルだ。
const filename = ‘document_2023.pdf’;
// 先頭が ‘document’ で始まるか?
console.log(filename.startsWith(‘document’)); // true
// 末尾が ‘.pdf’ で終わるか?
console.log(filename.endsWith(‘.pdf’)); // true
—
現場で差がつく「第二引数」の活用術
ここからが本題だ。中級エンジニアなら、`startsWith`と`endsWith`が第二引数(`position`)を受け取れることを知っておきたい。
`startsWith` の第二引数:検索開始位置の指定
例えば、URLのクエリパラメータや、特定のプレフィックスを飛ばして検索したい時に重宝する。
const path = ‘/api/v1/users/profile’;
// インデックス 9 以降(’users…’ の部分)から ‘users’ で始まるかチェック
// これを使えば、パスの階層を無視した柔軟なルーティング処理が可能になる
const isUserPath = path.startsWith(‘users’, 9);
console.log(isUserPath); // true
`endsWith` の第二引数:魔法の引数
個人的に一番面白いと思っているのがこれだ。`endsWith`の第二引数は、「どの位置までを文字列の長さとして扱うか」を指定する。
const rawData = ‘v1.0.0-beta’;
// 普通に使うとfalse
console.log(rawData.endsWith(‘v1.0.0’)); // false
// 第二引数に「ハイフンの位置」を渡すと…?
const hyphenIndex = rawData.indexOf(‘-‘);
console.log(rawData.endsWith(‘v1.0.0’, hyphenIndex)); // true
これは「文字列の切り出し(`slice`や`substring`)」を一切せずに、仮想的に文字列をカットして判定できる。パフォーマンス的にも、わざわざ新しい文字列インスタンスをメモリ上に生成しないから非常にスマートだ。
—
ブラウザの裏側で何が起きているのか?
少しだけアーキテクト的な話をしよう。JavaScriptエンジン(V8など)において、文字列は不変(Immutable)だ。`slice`や`substring`を乱用すると、そのたびに新しい文字列オブジェクトがヒープ領域に生成され、GC(ガベージコレクション)の負荷になる。
`startsWith`や`endsWith`は、内部的に文字列のポインタと長さを比較するだけで済むケースが多く、メモリ効率が良い。特に大規模なテキスト処理や、高頻度で呼ばれるバリデーションロジックにおいては、この小さな積み重ねがアプリケーション全体のレスポンスに効いてくる。
—
実践的ベストプラクティス:バリデーションへの応用
フォーム入力のバリデーションなどで、拡張子チェックやプロトコルチェックを実装する際は、以下のように関数化して使い回すのが現場の流儀だ。
/
- ファイルの拡張子が許可リストに含まれているか確認する
- @param {string} filename
- @param {string[]} allowedExtensions
/
const isValidExtension = (filename, allowedExtensions) => {
// 小文字に統一して判定するのが鉄則
const normalizedName = filename.toLowerCase();
// ひとつでも合致するものがあればOK
return allowedExtensions.some(ext => normalizedName.endsWith(ext));
};
const myFile = ‘report.PDF’;
console.log(isValidExtension(myFile, [‘.jpg’, ‘.pdf’, ‘.png’])); // true
—
最後に:先輩からのアドバイス
「たかが文字列判定」と思うかもしれない。でも、こういう些細なメソッドの選択一つひとつが、君のコードの「品格」を決めるんだ。
- 直感的なメソッド名でコードを書くこと
- 不要なオブジェクト生成を避けること
- `indexOf`の呪縛から解き放たれること
これらを意識するだけで、君の書くコードは確実に一段階上のレベルに到達する。次にプロジェクトのコードを触る時、`indexOf`を見つけたら、ぜひスマートな書き方にリファクタリングしてみてくれ。それが、技術の血を巡らせるということだ。
また何か気になったことがあればいつでも聞いてくれ。現場の最前線で、待っているよ。

コメント