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

おい、調子はどうだい?
最近、コードレビューをしていて「またここで無駄な正規表現書いてるな……」ってため息をつくことが増えたんだよね。例えば、URLの拡張子チェックや、ファイルパスの判定なんかでさ。

「文字列が特定の文字で終わるか」を調べたいだけなのに、わざわざ `str.match(/\.png$/)` なんて重い正規表現を持ち出したり、`str.indexOf(‘png’, str.length – 3) !== -1` なんていう、読むだけで頭痛がするような魔術的なマジックナンバーをコードに埋め込んだりしている現場に、君も遭遇したことがあるんじゃないかな?

今回は、そんな無駄な苦労をスパッと解消してくれる、JavaScriptの隠れた名脇役、`String.prototype.endsWith()` について徹底的に掘り下げていこうと思う。

中級からもう一段上のシニアへとステップアップしたい君に向けて、仕様の裏側から実務で絶対に役立つベストプラクティスまで、現場の空気感を交えて伝授するよ。最後までしっかりついてきてくれよな。

—

1. `endsWith()` とは何か?基本仕様とシニアが推す理由

まずは基本のおさらいからいこう。`endsWith()` は、ある文字列が指定された文字列で終わるかどうかを真偽値(`true` または `false`)で返すメソッドだ。

構文は非常にシンプルだ。

str.endsWith(searchString, length)

第一引数の `searchString` は「探したい終わりの文字列」。そして、見落とされがちだけど実務でめちゃくちゃ強力なのが第二引数の `length` だ。これ、「文字列全体の長さをここまでとみなす」という、いわば仮想的な切り詰め位置を指定できるんだよね。

なぜ `indexOf` や正規表現より優れているのか?

よくあるアンチパターンとして、以下のようなコードを見る。

// 昔ながらの indexOf を使った判定(可読性が低い)
const fileName = “report.pdf”;
const isPdf = fileName.indexOf(“.pdf”, fileName.length – 4) !== -1;

これ、”-4″ って何だっけ?ってなるよね。マジックナンバーの典型例だ。さらに正規表現を使うと、意図しないパフォーマンスの劣化を招くリスクや、特殊文字のエスケープ漏れによるバグの温床になる。

その点、`endsWith()` は意図がコードから一目瞭然だ。
「この文字列は、これで終わっているか?」というビジネスロジックの意図(Intent)が、そのままコードのリーダビリティに直結する。これが、俺たちが標準APIを愛する理由さ。

—

2. ブラウザの裏側はどう動いているか?(V8エンジン的な視点)

フロントエンドエンジニアなら、自分が書いたコードがブラウザのエンジン(V8など)でどう処理されるか、少しは気になるところだよな。

内部的な話をすると、`endsWith()` はECMAScriptの仕様に基づき、文字列の末尾から逆算して文字単位の比較を行っている。
内部的に正規表現エンジンを起動するわけではないため、純粋な文字列比較(Character Codeの比較)として非常に高速に動作する。

ただし、ここで一つだけ注意してほしい。JavaScriptの文字列はUTF-16でエンコードされている。つまり、サロゲートペア(絵文字や一部の漢字など、2つのコードポイントで1文字を表現するもの)を扱う場合、文字数(Length)と実際の「人間の見た目の文字数」が一致しないことがある。

例えば、絵文字の末尾判定を行うときは、単純なインデックス計算が狂うことがあるため、文字単位(Code Point単位)で処理されているかを意識するプロの視点が求められるんだ。もっとも、通常のファイル名やURL、ドメインの判定であれば、何の問題もなく爆速で動いてくれるから安心していい。

—

3. 【実務コピペOK】現場で即効性のある活用シーン

それじゃあ、実務の現場で「おっ、こいつ分かってるな」と思われるような具体的なユースケースをいくつかコードブロックで紹介しよう。そのままプロジェクトに持ち帰ってくれて構わない。

ユースケース A: アセットの拡張子チェック(画像やPDFのバリデーション)

アップロードされるファイル名や、動的に読み込むアセットのURL末尾を安全かつエレガントに弾くパターンだ。

/

  • ファイルが指定された画像形式のいずれかで終わっているか判定する
  • @param {string} url – チェック対象のファイルパスまたはURL
  • @returns {boolean}

/
function isAllowedImage(url) {
// 大文字小文字が混ざる現場の泥臭さに対応するため、小文字に統一して判定する
const lowerUrl = url.toLowerCase();

// 複数の候補がある場合は、some() と組み合わせるのがシニアの常套手段
const allowedExtensions = [‘.png’, ‘.jpg’, ‘.jpeg’, ‘.webp’];

return allowedExtensions.some(ext => lowerUrl.endsWith(ext));
}

// 実行例
console.log(isAllowedImage(“https://example.com/images/hero.JPG”)); // true (小文字化の恩恵)
console.log(isAllowedImage(“report.pdf”)); // false

ユースケース B: URLやパスの末尾スラッシュ(Trailing Slash)の正規化

ルーティングの処理やAPIエンドポイントの構築で、末尾のススラッシュの有無揺れに泣かされた経験はないかい? `endsWith()` を使えば、一発で綺麗にハンドリングできる。

/

  • APIのエンドポイントURLの末尾スラッシュを確実に除去する
  • @param {string} endpoint – 処理前のURL
  • @returns {string} 末尾スラッシュが綺麗に取れたURL

/
function sanitizeEndpoint(endpoint) {
// 末尾がスラッシュで終わっていれば、最後の1文字を削る
if (endpoint.endsWith(‘/’)) {
// ここで第二引数(length)が活きる! endpoint.length – 1 までを切り出す
return endpoint.slice(0, endpoint.length – 1);
}
return endpoint;
}

// 実行例
console.log(sanitizeEndpoint(“https://api.example.com/v1/users/”)); // “https://api.example.com/v1/users”
console.log(sanitizeEndpoint(“https://api.example.com/v1/users”)); // “https://api.example.com/v1/users”

※ちなみに、末尾スラッシュの削除なら `slice(-1)` や `replace(/\/$/, ”)` なんかも使えるけれど、条件分岐を明示的に書いて「ここでスラッシュを落としています」と意図を伝えるには `endsWith()` の組み合わせが一番レビューしやすいんだよね。

—

4. ありがちな罠:TypeScriptや古い環境での注意点

最後に、実務でハマりがちなポイントを共有しておく。

1. 型エラー(TypeScript):
TypeScriptで `strictNullChecks` を有効にしている環境で、対象の変数が `null` や `undefined` の可能性がある場合、いきなり `str.endsWith(…)` を呼ぶと容赦なく型エラー(あるいは実行時エラー)でアプリがクラッシュする。
必ずオプショナルチェイニング (`str?.endsWith(…)`) を使うか、事前に文字列型であることをガードしよう。
2. Polyfillの必要性:
現代のモダンブラウザやNode.js環境であれば間違いなく標準サポートされている(IE11?もう令和の現場には存在しないよね?もし残っていたら……ご愁傷様、Babelとcore-jsを入れよう)。

—

まとめ

`String.prototype.endsWith()` は、地味ながらもコードの「意図の明確さ」と「保守性」を劇的に引き上げてくれる素晴らしいメソッドだ。

無駄に複雑な正規表現を書く前に、まずは標準の文字列メソッドでシンプルに解決できないか立ち止まってみる。そういう「引き出しの多さ」が、現場で頼られるフロントエンドエンジニアへの第一歩だからね。

それじゃ、今日のレビューはこの辺で。また次の知見で会おう!

コメント

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