おい、調子はどうだ?
今日も元気にエディタに向かってコードを叩きまくっていることだろう。
今回は、JavaScriptの文字列操作の基本でありながら、現場のコードレビューで意外と「あぁ、惜しいな……」とツッコミを入れたくなるポイントが多い `String.prototype.indexOf()` について話をしよう。
中級への階段を登っている君なら、「文字列の中から特定の部分文字列を探すやつでしょ?知ってるよ」と思うかもしれない。だが、この古臭く見えるメソッドの裏側の仕様や、モダンなJavaScriptにおける立ち位置、そして「見つからない場合の `-1`」という癖の強い戻り値とどう向き合うべきか。ここを深く理解しているかどうかで、君が書くコードの「ロバスト性(堅牢性)」が一段も二段も変わってくるんだ。
さあ、コーヒーでも飲みながら、現場のリアルな知見に付き合ってくれ。
—
1. なぜ今さら `indexOf()` なのか?仕様の基本とブラウザの裏側
まずは基本の復習からいこう。
`str.indexOf(searchValue[, fromIndex])` は、呼び出した文字列内で `searchValue` が最初に現れるインデックス(0始まり)を返す。見つからなければ `-1` だ。
const message = “Hello, Frontend Architecture!”;
console.log(message.indexOf(“Frontend”)); // 7
console.log(message.indexOf(“Backend”)); // -1 (-1が返ってくる)
ここで、少しシニアらしい「裏側の話」をしておこう。
JavaScriptのエンジン(Google ChromeのV8など)は、この文字列検索をどう処理していると思う? 「ただ前から順番に文字を比較しているだけなんでしょ?」と思ったら大間違いだ。
内部的には、C++レベルで最適化されたメモリスキャンや、 Boyer-Moore法 や Shift-AND法 といった高速な文字列検索アルゴリズムが使われているケースが多い。特に長い文字列(例えば数千文字あるAPIのレスポンスログなど)を検索する際、JavaScript側で愚直に `for` ループを回すよりも、ネイティブ実装である `indexOf()` に任せた方が圧倒的にパフォーマンスが良い。
「文字列の位置を知りたい」という目的なら、車輪の再発明をせずに素直にこれを使うのが正解だ。
—
2. 現場でやってはいけない! `-1` 判定のアンチパターン
さて、ここからが本題だ。`indexOf()` を語る上で避けて通れないのが、「見つからない場合に `-1` を返す」という設計上の仕様だ。これが原因で、現場では数々のバグが生まれてきた。
よく見かける、絶対にやってはいけないアンチパターンを挙げてみよう。
// ❌ 良くないコード:もし indexOf の結果が 0(先頭で見つかった)だった場合…
const url = “/api/v1/users”;
if (url.indexOf(“/api”)) {
// 処理を実行したい!
console.log(“APIリクエストです”);
}
おっと、君ならこのコードのバグの匂いに気づくだろうか?
`url.indexOf(“/api”)` の結果は、文字列の先頭にあるため `0` になる。
しかし、JavaScriptにおいて `0` は 「Falsy(偽と評価される値)」 だ。そのため、`if (0)` は `false` となり、「APIリクエストです」というログは一生出力されない。
これ、実際の現場のコードレビューで本当に見かけるミスなんだ。「あ、動かないな?」と焦って、さらに泥臭いハックを重ねてカオスなコードになっていく……なんて悪夢を何度も目撃してきた。
—
3. 実務で使える! 正しい判定とモダンな代替手段
じゃあ、どう書くのがプロとしてスマートなのか?
シニアとして、いくつかのベストプラクティスを授けよう。
パターンA: `-1` と厳密に比較する(王道・安全)
まずは、`indexOf()` をそのまま使う場合の正しい作法だ。`!== -1` を使って、暗黙の型変換に頼らない厳密な比較を行うこと。
const path = “/settings/profile”;
// 厳密不等価演算子 (!==) を使い、-1 ではないことを明確に担保する
if (path.indexOf(“/settings”) !== -1) {
console.log(“設定画面のパスが含まれています。”);
}
パターンB: そもそも「位置」が不要なら `includes()` を使え
「文字列が含まれているかどうか(真偽値)」だけが知りたいのに、わざわざ `indexOf() !== -1` と書くのは、モダンなJavaScript(ES2015以降)の恩恵をドブに捨てるようなものだ。
真偽値が欲しいなら、迷わず `String.prototype.includes()` を使おう。
const userAgent = “Mozilla/5.0 (iPhone; CPU iPhone OS 16_0 like Mac OS X)…”;
// 位置を気にする必要がないなら includes 一択。コードの意図が明確になる
if (userAgent.includes(“iPhone”)) {
console.log(“iOSからのアクセスを検知しました。”);
}
`includes()` のおかげで、さっきの `0` が `false` に化けるようなバグとはおさらばできる。可読性も爆上がりだ。
—
4. 実戦投入:実務で役立つユーティリティ関数
最後に、実際のフロントエンド開発(フォームのバリデーションやURL解析など)でそのままコピペして使える、ちょっと気の利いたユーティリティ関数のコードを置いておく。
/
- 指定したドメインが安全な許可リスト(Allowlist)に含まれているかチェックする関数
- @param {string} targetUrl – チェック対象のURL
- @param {string[]} allowedDomains – 許可されたドメインの配列
- @returns {boolean}
/
function isTrustedDomain(targetUrl, allowedDomains) {
// ガード節:文字列以外が渡された場合の防衛
if (typeof targetUrl !== “string”) return false;
// indexOf をあえて使うケース:
// 「ドメイン名がURLのどこに含まれているか」だけでなく、
// 「先頭(プロトコル直後)から正しく始まっているか」を厳密にチェックしたい場合など。
// 今回はシンプルに includes の組み合わせで実装例を示す。
// 高度な検索が必要な場合は indexOf で位置(例: 0番目から始まっているかなど)を検証する
return allowedDomains.some(domain => {
const index = targetUrl.indexOf(domain);
// 例として、「ドメインが含まれており、かつそれが先頭に近い位置にあるか」を検証
// (実際のURLパースには URL APIを使うべきだが、indexOfの応用例として)
return index !== -1 && index <= 8;
});
}
// --- 動作確認用テストケース ---
const whitelist = ["example.com", "trusted-site.org"];
console.log(isTrustedDomain("https://example.com/dashboard", whitelist));
// 出力: true (インデックスが8番目以内に見つかる)
console.log(isTrustedDomain("https://evil.com?ref=example.com", whitelist));
// 出力: false (含まれてはいるが、安全な位置(先頭付近)にドメインが来ていないため弾く)
このコードのポイントは、単に `indexOf() !== -1` で存在確認をするだけでなく、「文字列内のどの位置(Index)にあるか」という情報を条件分岐のスパイスとして利用している点だ。`indexOf()` の真価は、まさにこの「位置情報の取得」にある。
—
まとめ
さて、ここまで `String.prototype.indexOf()` について語ってきたが、要点をまとめておこう。
1. `indexOf()` は「位置」を返すメソッドであり、見つからない場合は `-1` を返す。
2. `if (str.indexOf(…) )` のように、戻り値の `0` を Falsy として踏み抜くバグに細心の注意を払う。
3. 位置が不要で「含まれているか」だけを知りたいなら、素直に `includes()` を使え。
4. 「どこから始まるか」という位置の制約や、パフォーマンスが必要なスキャンには `indexOf()` を現役バリバリで活用していく。
基礎的なメソッドほど、その仕様の裏側や落とし穴を知っているかどうかで、エンジニアとしての「格」が出るものだ。
今日の話を頭の片隅に置いて、明日からのコードレビューや実装に活かしてほしい。
それじゃあ、また次の現場でお会いしよう。ハッピー・コーディング!

コメント