Lập trình · 20/10/2026

includes và indexOf trong JavaScript: vì sao cùng tìm một giá trị lại khác kết quả?

Đổi indexOf(value) !== -1 thành includes(value) thường làm mã dễ đọc hơn, nhưng không phải lúc nào cũng giữ nguyên hành vi. Trước khi thay hàng loạt, hãy xem kiểu dữ liệu, ô trống và phạm vi tìm kiếm. Bài này chỉ xét phương thức của Array, không phải tìm chuỗi con trong String.

includes và indexOf trong JavaScript: vì sao cùng tìm một giá trị lại khác kết quả?

Đổi indexOf(value) !== -1 thành includes(value) thường làm mã dễ đọc hơn, nhưng không phải lúc nào cũng giữ nguyên hành vi. Trước khi thay hàng loạt, hãy xem kiểu dữ liệu, ô trống và phạm vi tìm kiếm. Bài này chỉ xét phương thức của Array, không phải tìm chuỗi con trong String.

1. Boolean và vị trí phục vụ hai nhu cầu khác nhau

const statuses = ['queued', 'running'];
console.log(statuses.includes('queued')); // true
console.log(statuses.indexOf('queued')); // 0
console.log(Boolean(statuses.indexOf('queued'))); // false
console.log(Boolean(statuses.indexOf('missing'))); // true

Nếu cần biết có phần tử hay không, includes trả boolean trực tiếp. indexOf trả vị trí hoặc -1; dùng kết quả đó như boolean sẽ sai ở cả vị trí 0 lẫn trường hợp không tìm thấy. Nếu cần vị trí để xử lý tiếp, giữ indexOf và so sánh rõ với -1.

2. NaN là khác biệt cần kiểm tra khi chuyển đổi

const values = [NaN, 0, '2'];
console.log(values.includes(NaN)); // true
console.log(values.indexOf(NaN)); // -1
console.log(values.includes(-0)); // true
console.log(values.includes(2)); // false

includes dùng quy tắc SameValueZero, nên tìm được NaN và coi +0 bằng -0. indexOf dùng so sánh strict equality, không tìm được NaN. Cả hai không tự coi chuỗi '2' là số 2. Đây là lý do cần chạy lại kiểm thử dữ liệu số đặc biệt khi thay phương thức.

3. Ô trống không giống phần tử undefined

const sparse = new Array(1);
console.log(sparse.includes(undefined)); // true
console.log(sparse.indexOf(undefined)); // -1
console.log([undefined].indexOf(undefined)); // 0

Với mảng thưa thông thường không có thuộc tính chỉ mục kế thừa, includes đọc ô trống như undefined; indexOf bỏ qua ô không tồn tại. Vì vậy, includes(undefined) không chứng minh mảng có phần tử undefined được gán tường minh. Nếu ô trống là dữ liệu lỗi, hãy kiểm tra cấu trúc đầu vào riêng.

4. Object giống nội dung vẫn khác tham chiếu

const saved = { id: 'a1' };
const rows = [saved];
console.log(rows.includes(saved)); // true
console.log(rows.includes({ id: 'a1' })); // false
console.log(rows.some(row => row.id === 'a1')); // true

Ví dụ cho thấy cùng khóa id chưa đủ để includes coi hai object là một. Khi nghiệp vụ hỏi bản ghi có ID này hay chưa, biểu thức some thể hiện đúng tiêu chí hơn. Dữ liệu thực tế cần được kiểm tra schema trước; nếu danh sách có null, việc đọc row.id trực tiếp có thể ném lỗi.

5. fromIndex âm vẫn tìm tiến về cuối mảng

const letters = ['a', 'b', 'a', 'c'];
console.log(letters.includes('a', -2)); // true
console.log(letters.indexOf('a', -2)); // 2
console.log(letters.includes('b', -2)); // false
console.log(letters.includes('a', 4)); // false

Với ví dụ dài 4 phần tử, -2 đặt điểm bắt đầu tại chỉ mục 2. Việc tìm kiếm vẫn đi về cuối, không quay ngược. Khi cần lần xuất hiện cuối cùng, cân nhắc lastIndexOf với quy tắc riêng thay vì suy diễn từ dấu âm.

6. Checklist trước khi sửa mã

  • Chọn rõ kết quả boolean hay chỉ mục.
  • Thử phần tử đầu tiên và giá trị không tồn tại.
  • Thử NaN, số 0 và chuỗi số.
  • Phân biệt mảng thưa với undefined tường minh.
  • Xác định so sánh object theo tham chiếu hay theo khóa.
  • Giữ nguyên phạm vi fromIndex mong muốn.

Mười khối ví dụ Việt–Anh đã chạy với Node.js, kèm bảng kiểm tra đối chiếu hai phương thức. Không đo hiệu năng hoặc kiểm thử giao diện trình duyệt. Tham khảo MDN Array.includes và đặc tả ECMAScript cho phương thức mảng.

Thảo luận

Bình luận 0

Đăng nhập để bình luận

Bạn cần có tài khoản để tham gia thảo luận và trả lời độc giả khác.

Đăng nhậpĐăng ký

Chưa có bình luận. Hãy là người đầu tiên chia sẻ ý kiến.