【JavaScript】indexOf の使い方|文字列・配列の位置検索・lastIndexOf・全出現位置の取得・includes との違いまで解説

indexOf は文字列や配列の中から指定した値が最初に見つかった位置(インデックス)を返すメソッドです。見つからなかった場合は -1 を返します。文字列の解析、配列の要素検索、slice との組み合わせによる部分文字列の抽出など、実務で使う場面が非常に多いメソッドです。

この記事でわかること
・文字列の indexOf の基本構文と戻り値
・検索開始位置(第 2 引数)の指定方法
・見つからない場合の -1 判定パターン
・lastIndexOf で末尾から検索する方法
・配列の indexOf の基本と注意点
・大文字・小文字を無視して検索する方法
・全出現位置を配列で取得する方法
・includes との使い分け
スポンサーリンク

文字列の indexOf の基本

構文
const index = str.indexOf(searchString, fromIndex);
// searchString: 検索する文字列
// fromIndex:    検索開始位置(省略可、デフォルト 0)
// 戻り値:       最初に見つかった位置(0 始まり)、見つからなければ -1
基本例
const str = "JavaScript is awesome";

console.log(str.indexOf("Script")); // 4
console.log(str.indexOf("is"));     // 11
console.log(str.indexOf("Python")); // -1(見つからない)
console.log(str.indexOf("a"));      // 1(最初の "a" の位置)

検索開始位置を指定する

第 2 引数 fromIndex を指定すると、その位置以降から検索を開始します。同じ文字列の 2 番目以降の出現位置を探すときに使います。

JavaScript
const str = "abcabc";

console.log(str.indexOf("abc"));     // 0(最初の出現)
console.log(str.indexOf("abc", 1));  // 3(1番目以降で次の出現)
console.log(str.indexOf("abc", 4));  // -1(4番目以降には見つからない)

空文字列を検索した場合

JavaScript
const str = "Hello";

// 空文字列は常に見つかる
console.log(str.indexOf(""));     // 0
console.log(str.indexOf("", 3));  // 3(fromIndex がそのまま返る)
console.log(str.indexOf("", 10)); // 5(文字列の長さが上限)

見つからない場合の -1 判定

indexOf の戻り値で最も重要なのは -1(見つからない)の判定です。

JavaScript
const url = "https://example.com/search?q=javascript";

// 含まれているかの判定
if (url.indexOf("?") !== -1) {
  console.log("クエリパラメータあり");
}

// 含まれていない場合の判定
if (url.indexOf("#") === -1) {
  console.log("ハッシュなし");
}
indexOf はインデックス 0 で見つかると 0(falsy)を返します。if (str.indexOf("x")) と書くと、0 番目で見つかった場合に false 扱いになるバグの原因になります。必ず === -1 または !== -1 で比較してください。
NG: 0 が falsy になるバグ
const str = "abc";

// NG: indexOf が 0 を返すと false 扱いになる
if (str.indexOf("a")) {
  console.log("見つかった"); // 実行されない!
}

// OK: -1 と比較する
if (str.indexOf("a") !== -1) {
  console.log("見つかった"); // 正しく実行される
}

lastIndexOf で末尾から検索する

lastIndexOf は文字列(または配列)の末尾側から検索し、最後に見つかった位置を返します。戻り値のインデックスは先頭から数えた値です。

JavaScript
const path = "/home/user/documents/file.txt";

// 最後の "/" の位置
console.log(path.lastIndexOf("/"));  // 20

// 最後の "." の位置(拡張子の区切り)
console.log(path.lastIndexOf(".")); // 30

// indexOf と比較
console.log(path.indexOf("/"));      // 0(最初の "/")
console.log(path.lastIndexOf("/"));  // 20(最後の "/")
ファイル名と拡張子の抽出
const filepath = "/images/photo-2026.jpg";

// ファイル名(最後の / 以降、拡張子なし)
const slashPos = filepath.lastIndexOf("/");
const dotPos = filepath.lastIndexOf(".");
const filename = filepath.slice(slashPos + 1, dotPos);
console.log(filename); // "photo-2026"

// 拡張子
const ext = filepath.slice(dotPos);
console.log(ext); // ".jpg"

配列の indexOf

配列にも同名の indexOf メソッドがあり、指定した値が最初に見つかったインデックスを返します。

JavaScript
const colors = ["red", "green", "blue", "green"];

console.log(colors.indexOf("green")); // 1(最初の出現)
console.log(colors.indexOf("yellow")); // -1(見つからない)

// 検索開始位置を指定
console.log(colors.indexOf("green", 2)); // 3(2番目以降の出現)

オブジェクト配列では使えない

配列の indexOf は厳密等価(===)で比較するため、同じ内容でも別のオブジェクトインスタンスでは見つかりません。

JavaScript
const users = [{ id: 1 }, { id: 2 }];

// 別のオブジェクト → 見つからない
console.log(users.indexOf({ id: 1 })); // -1

// 同じ参照なら見つかる
const target = users[0];
console.log(users.indexOf(target)); // 0

// オブジェクトの内容で検索するなら findIndex を使う
console.log(users.findIndex(u => u.id === 1)); // 0

NaN は見つけられない

JavaScript
const arr = [1, NaN, 3];

console.log(arr.indexOf(NaN));  // -1(見つけられない)
console.log(arr.includes(NaN)); // true(includes は見つけられる)
配列で NaN の存在チェックが必要なら includes、オブジェクトの内容で検索するなら findIndex を使いましょう。

大文字・小文字を無視して検索する

indexOf は大文字・小文字を区別します。区別せずに検索したい場合は、両方を小文字に変換してから検索します。

JavaScript
function indexOfIgnoreCase(str, search) {
  return str.toLowerCase().indexOf(search.toLowerCase());
}

const text = "Hello JavaScript World";
console.log(indexOfIgnoreCase(text, "javascript")); // 6
console.log(indexOfIgnoreCase(text, "HELLO"));      // 0

全出現位置を配列で取得する

indexOf を繰り返し呼び出すことで、文字列中のすべての出現位置を配列として取得できます。

while ループ版
function findAllIndexes(str, search) {
  const positions = [];
  let pos = -1;

  while ((pos = str.indexOf(search, pos + 1)) !== -1) {
    positions.push(pos);
  }

  return positions;
}

console.log(findAllIndexes("abcabcabc", "abc")); // [0, 3, 6]
console.log(findAllIndexes("banana", "an"));      // [1, 3]
matchAll 版(正規表現)
const str = "abcabcabc";
const positions = [...str.matchAll(/abc/g)].map(m => m.index);
console.log(positions); // [0, 3, 6]

indexOf と includes の使い分け

比較項目 indexOf includes
戻り値 インデックス(見つからなければ -1) true / false
NaN の判定 見つけられない 見つけられる
位置の取得 できる できない
可読性 !== -1 が冗長 直感的
IE 対応 IE 対応 IE 非対応
使い分け
const str = "Hello World";

// 「含まれているか」だけを知りたい → includes
if (str.includes("World")) { /* ... */ }

// 「どこにあるか」を知りたい → indexOf
const pos = str.indexOf("World");
if (pos !== -1) {
  const before = str.slice(0, pos);
  console.log(before); // "Hello "
}
存在チェックだけなら includes、位置が必要なら indexOf。この使い分けを徹底するとコードの可読性が上がります。

実務でよく使うパターン

URL からクエリパラメータ部分を抽出する

JavaScript
function getQueryString(url) {
  const qPos = url.indexOf("?");
  if (qPos === -1) return "";
  return url.slice(qPos + 1);
}

console.log(getQueryString("https://example.com/search?q=js&lang=ja"));
// "q=js&lang=ja"

メールアドレスからユーザー名とドメインを分離する

JavaScript
function splitEmail(email) {
  const atPos = email.indexOf("@");
  if (atPos === -1) return null;

  return {
    user: email.slice(0, atPos),
    domain: email.slice(atPos + 1)
  };
}

console.log(splitEmail("tanaka@example.com"));
// { user: "tanaka", domain: "example.com" }

特定のキーワードをハイライトする

JavaScript
function highlight(text, keyword) {
  const lowerText = text.toLowerCase();
  const lowerKey = keyword.toLowerCase();
  const pos = lowerText.indexOf(lowerKey);

  if (pos === -1) return text;

  const before = text.slice(0, pos);
  const match = text.slice(pos, pos + keyword.length);
  const after = text.slice(pos + keyword.length);

  return `${before}<mark>${match}</mark>${after}`;
}

console.log(highlight("JavaScript is great", "script"));
// "Java<mark>Script</mark> is great"

関連記事

よくある質問

QindexOf で見つからない場合の -1 をチェックし忘れるとどうなりますか?
A-1 のまま slicesubstring に渡すと、-1 が末尾からの位置として解釈されたり 0 に変換されたりして、意図しない結果になります。indexOf の戻り値は必ず === -1 でチェックしてから使いましょう。
QindexOf と lastIndexOf の違いは何ですか?
AindexOf は先頭から検索して最初の出現位置を返し、lastIndexOf は末尾から検索して最後の出現位置を返します。ファイルパスの拡張子取得(最後の .)やディレクトリ取得(最後の /)には lastIndexOf が適しています。
QindexOf は正規表現を使えますか?
Aいいえ、indexOf は正規表現に対応していません。正規表現で位置を検索したい場合は str.search(/pattern/) を使います。ただし search は検索開始位置を指定できないため、全出現位置の取得には matchAll が適しています。
QindexOf と findIndex はどう違いますか?
AindexOf(value) は値の一致で検索し、findIndex(fn) はコールバック関数の条件で検索します。プリミティブ値の配列には indexOf、オブジェクト配列やカスタム条件には findIndex を使い分けます。
QindexOf は大文字・小文字を区別しますか?
Aはい、区別します。区別せずに検索したい場合は str.toLowerCase().indexOf(search.toLowerCase()) のように両方を小文字に変換してから検索します。

まとめ

indexOf は文字列と配列の両方で使える位置検索メソッドです。

  • 基本: 最初に見つかった位置を返す。見つからなければ -1
  • lastIndexOf: 末尾から検索して最後の出現位置を返す
  • -1 判定: !== -1 で必ず比較する(0 が falsy になるバグ防止)
  • 配列の注意点: オブジェクトは参照比較、NaN は見つけられない
  • includes との使い分け: 存在チェックだけなら includes、位置が必要なら indexOf

slice や substring との組み合わせで文字列の解析(URL解析・メール分割・拡張子取得)に威力を発揮します。戻り値の -1 チェックを忘れないことが、indexOf を安全に使うための最重要ポイントです。