JavaScript
Arrays
includes
เรียนรู้ includes() — method เช็คว่า element มีอยู่ใน Array หรือไม่โดยคืนค่า boolean ตรง ๆ ตั้งแต่การแทนที่ indexOf() !== -1 ให้อ่านง่ายขึ้น, การเลือกใช้ระหว่าง includes กับ indexOf ให้เหมาะกับสถานการณ์, SameValueZero algorithm ที่ทำให้หา NaN ได้ (ต่างจาก indexOf), fromIndex ควบคุมช่วงค้นหา, ไปจนถึงข้อควรระวังเรื่อง type/case sensitivity ผ่าน Lab 3 ข้อ
`includes()` คืออะไร — เช็ค boolean ว่า element มีอยู่ใน array
includes() เป็น method ใน array ใช้ตรวจสอบว่ามี element ที่เราต้องการอยู่ใน array หรือไม่ — คืนค่า (return) เป็น boolean (true/false) ตรง ๆ ไม่ต้องมาเช็ก -1 แบบ indexOf สิ่งที่ต้องรู้: • คืนค่า true ถ้าเจอ element, false ถ้าไม่เจอ • ค้นหาจากซ้ายไปขวา (index 0 → index สุดท้าย) • รับ parameter ได้ 2 ตัว: searchElement (จำเป็น) และ fromIndex (ไม่จำเป็น) • ใช้ SameValueZero algorithm ในการเปรียบเทียบ (ต่างจาก indexOf ที่ใช้ ===) • เจอ NaN ได้ — ข้อได้เปรียบสำคัญเหนือ indexOf()
const fruits = ["แอปเปิ้ล", "กล้วย", "ส้ม"];
// includes() คืนค่า boolean — ไม่ต้องแปลง -1 อีก
console.log(fruits.includes("กล้วย")); // true — มี "กล้วย" ใน array
console.log(fruits.includes("มะม่วง")); // false — ไม่มี "มะม่วง"
// === เปรียบเทียบกับ indexOf: ต้องเขียนยาวและมีโอกาสพลาด
if (fruits.indexOf("กล้วย") !== -1) {
// แบบเก่า — ต้องจำว่า !== -1 แปลว่ามี
}
if (fruits.includes("กล้วย")) {
// แบบใหม่ — อ่านแล้วเข้าใจทันทีว่า "มีกล้วยหรือเปล่า"
}จุดเด่นของ includes(): อ่านโค้ดแล้วเข้าใจทันทีว่าเช็คอะไร — `if (arr.includes(x))` ชัดเจนกว่า `if (arr.indexOf(x) !== -1)` มาก สำคัญ: includes() คืนค่าจริง (true) หรือเท็จ (false) เท่านั้น — ไม่ได้คืน index ไม่ได้คืน element — ถ้าอยากรู้ตำแหน่งด้วยต้องใช้ indexOf()
const roles = ["admin", "editor", "viewer"];
// ตรวจสอบว่าผู้ใช้มี role admin หรือไม่
if (roles.includes("admin")) {
console.log("ยินดีต้อนรับ admin ✓");
// ทำ action สำหรับ admin — เช่น แสดงเมนูจัดการระบบ
}
// ตรวจสอบว่า role ไม่อยู่ใน blacklist
const blocked = ["banned", "suspended"];
const userRole = "editor";
if (!blocked.includes(userRole)) {
console.log("role อนุญาตให้เข้าใช้งาน ✓");
// userRole = "editor" — ไม่อยู่ใน blocked → ผ่าน
}กฎสำคัญของ includes(): • รับ searchElement 1 ตัว — จะเช็คว่ามีค่านี้อยู่ใน array หรือไม่ • คืนค่า true ทันทีที่เจอตัวแรก — และหยุดค้นหา • ใช้กับ array เท่านั้น — ไม่ใช่ string method (string มี includes() เช่นกัน แต่คนละ method) • fromIndex ทำงานเหมือนกับ indexOf — กำหนดจุดเริ่มค้นได้
เลือก `includes()` หรือ `indexOf()` — ใช้เมื่อไหร่
ทั้ง includes() และ indexOf() ใช้ค้นหา element ใน array — แต่ตอบคำถามคนละแบบ การเลือกใช้ขึ้นอยู่กับว่าคุณต้องการคำตอบแบบไหน กฎง่าย ๆ: • อยากรู้ว่า "มี element นี้หรือไม่?" → ใช้ includes() • อยากรู้ว่า "element นี้อยู่ที่ index อะไร?" → ใช้ indexOf() • อยากรู้ทั้งคู่ — "มีหรือไม่?" + "อยู่ที่ไหน?" → ใช้ indexOf() เพราะได้ index มาใช้ต่อได้เลย
| เรื่อง | includes() | indexOf() |
|---|---|---|
| return | boolean (true / false) | number (index หรือ -1) |
| คำถามที่ตอบ | "มี element นี้หรือไม่?" | "element นี้อยู่ที่ index อะไร?" |
| ใช้ใน if โดยตรง | ใช่ — if (arr.includes(x)) อ่านง่าย | ต้องเช็ก !== -1 — if (arr.indexOf(x) !== -1) |
| เมื่อต้องการ index | ไม่เหมาะ — ต้องใช้ indexOf() แทน | ได้ index ไปใช้ต่อทันที เช่น arr[index] |
| หา NaN ได้? | ได้ — ใช้ SameValueZero | ไม่ได้ — ใช้ === (NaN !== NaN) |
| ความเร็ว | เร็วใกล้เคียงกัน — O(n) เหมือนกัน | เร็วใกล้เคียงกัน — O(n) เหมือนกัน |
| รองรับเบราว์เซอร์ | ES2016 (ES7) — เบราว์เซอร์ใหม่ | มีมาตั้งแต่ ES3 — เก่าที่สุด |
const tags = ["javascript", "array", "method"];
// === เมื่ออยากรู้แค่ว่า "มีหรือไม่?" → includes สั้นกว่า
// includes — เขียนสั้น อ่านง่าย
if (tags.includes("javascript")) {
console.log("มี tag javascript ✓");
}
// indexOf — เขียนยาว แต่ก็ใช้ได้
if (tags.indexOf("javascript") !== -1) {
console.log("มี tag javascript ✓");
}
// === เมื่ออยากรู้ว่า "อยู่ที่ index อะไร?" → ต้องใช้ indexOf
const idx = tags.indexOf("array");
if (idx !== -1) {
// ใช้ index ที่หาได้ทำงานต่อ
console.log("พบ 'array' ที่ index", idx);
const upper = tags[idx].toUpperCase();
console.log(upper); // ARRAY
}
// includes ไม่ได้คืน index — ใช้ตรงนี้ไม่ได้
// tags.includes("array") → true เท่านั้น ไม่ได้ indexสรุปการเลือกใช้: ใช้ includes() เมื่อ: • ตรวจสอบว่ามี element หรือไม่ (existence check) • ต้องการ boolean เพื่อใช้ใน if หรือเงื่อนไขโดยตรง • เขียนโค้ดให้อ่านง่าย ลดการสะกดผิด !== -1 • ต้องการตรวจสอบว่ามี NaN ใน array หรือไม่ (indexOf หา NaN ไม่ได้) ใช้ indexOf() เมื่อ: • ต้องการรู้ตำแหน่ง (index) ของ element • ต้องการนำ index ที่หาได้ไปใช้ต่อ — อ่านหรืออัปเดต element นั้น • ต้อง support เบราว์เซอร์เก่ามากที่ไม่มี includes()
`includes()` ใช้ SameValueZero — ข้อแตกต่างจาก `indexOf()` ที่ใช้ `===`
includes() ใช้ SameValueZero ในการเปรียบเทียบ — ต่างจาก indexOf() ที่ใช้ Strict Equality (===) ความแตกต่างที่สำคัญที่สุด: SameValueZero ทำให้ includes() หา NaN ได้ — เพราะ SameValueZero มองว่า NaN เท่ากับ NaN (ต่างจาก === ที่ NaN !== NaN) แต่ในแง่ของ type และ case sensitivity — SameValueZero กับ === ให้ผลลัพธ์เหมือนกัน
| searchElement | element ใน array | includes() | indexOf() | เพราะว่า |
|---|---|---|---|---|
| 42 | 42 | true | 0 (เจอ) | === และ SameValueZero: number เท่ากัน |
| "42" | 42 | false | -1 (ไม่เจอ) | ทั้งคู่มองว่า type ต่างกัน — string ≠ number |
| "hello" | "hello" | true | 0 (เจอ) | string เหมือนกัน |
| "Hello" | "hello" | false | -1 (ไม่เจอ) | ตัวพิมพ์เล็กใหญ่ต่างกัน — "H" ≠ "h" |
| NaN | NaN | ⭐ true | -1 (ไม่เจอ) | SameValueZero: NaN = NaN / ===: NaN ≠ NaN |
| undefined | undefined | true | 0 (เจอ) | ทั้งคู่: undefined === undefined |
| null | null | true | 0 (เจอ) | ทั้งคู่: null === null |
| +0 | -0 | true | 0 (เจอ) | SameValueZero: +0 = -0 / ===: +0 = -0 |
const nums = [NaN, 1, 2, 3];
// === includes() — เจอ NaN ✓
console.log(nums.includes(NaN)); // true ← SameValueZero: NaN = NaN
// === indexOf() — เจอ NaN ไม่ได้ ✗
console.log(nums.indexOf(NaN)); // -1 ← ===: NaN !== NaN
// === เมื่อมี NaN หลายตัว — includes เจอตัวแรก
const more = [1, NaN, 2, NaN];
console.log(more.includes(NaN)); // true — เจอ NaN ตัวแรกหยุด
// === ใช้ includes เช็ค NaN ใน conditional
const values = [1, NaN, 3];
if (values.includes(NaN)) {
console.log("มี NaN ใน array — ควรตรวจสอบข้อมูล ✓");
}
// indexOf ไม่สามารถทำแบบนี้ได้ — มัน return -1const mixed = [1, "1", "hello", "Hello"];
// === type sensitivity — ทั้งคู่มองว่า 1 ≠ "1"
console.log(mixed.includes(1)); // true — number 1 มีอยู่
console.log(mixed.includes("1")); // true — string "1" มีอยู่
// indexOf ให้ผลเหมือน: index 0 และ 1 ตามลำดับ
// === case sensitivity — ทั้งคู่มองว่า "hello" ≠ "Hello"
console.log(mixed.includes("hello")); // true — "hello" มีอยู่ที่ index 2
console.log(mixed.includes("Hello")); // true — "Hello" มีอยู่ที่ index 3
console.log(mixed.includes("HELLO")); // false — ตัวพิมพ์ใหญ่ทั้งหมดไม่มี
// === number vs string ที่หน้าตาเหมือนกัน
const ids = [100, 200, 300];
console.log(ids.includes(100)); // true — number
console.log(ids.includes("100")); // false — string ≠ number
// type ต่างกัน → ถือว่าไม่เจอ (เหมือน indexOf)ข้อควรรู้เพิ่มเติม: Object และ Array — includes() ใช้ SameValueZero ซึ่งก็ยังเทียบ reference ไม่ใช่ค่า: • [{name: 'A'}].includes({name: 'A'}) → false — เพราะคนละ reference • การหา object/array ด้วย includes() ต้องเป็น reference เดียวกันเท่านั้น เช่น const obj = {}; [obj].includes(obj) → true ถ้าต้องการหา object ด้วยค่าข้างใน — ต้องใช้ findIndex() หรือ find() (จะเรียนในบท array methods)
ใช้ `fromIndex` กับ `includes()` — เริ่มค้นจากตำแหน่งที่กำหนด
includes() รับ parameter ตัวที่สองชื่อ fromIndex — ทำงานเหมือนกับของ indexOf() ทุกประการ ประโยชน์: • ตรวจสอบว่า element มีอยู่ตั้งแต่ตำแหน่งใดตำแหน่งหนึ่งเป็นต้นไปหรือไม่ • ข้ามข้อมูลช่วงแรกที่รู้ว่าไม่เกี่ยวข้อง • เช็คข้อมูลใน slice ย่อยของ array โดยไม่ต้อง copy array ใหม่
const items = ["A", "B", "A", "C", "A"];
// index: 0 1 2 3 4
// ไม่ระบุ fromIndex — เริ่มจาก 0 (default)
console.log(items.includes("A")); // true — มี "A" ที่ index 0
// fromIndex = 1 — เช็คตั้งแต่ index 1 เป็นต้นไป
console.log(items.includes("A", 1)); // true — มี "A" ที่ index 2
// fromIndex = 3 — เช็คตั้งแต่ index 3 เป็นต้นไป
console.log(items.includes("A", 3)); // true — มี "A" ที่ index 4
// fromIndex = 5 — เริ่มตั้งแต่ index 5 (เกิน length)
console.log(items.includes("A", 5)); // false — ไม่มี element ตั้งแต่ index 5const chars = ["X", "Y", "Z", "Y", "W"];
// index: 0 1 2 3 4
// จากท้าย: -5 -4 -3 -2 -1
// fromIndex = -2 → เริ่มที่ index 3 (length - 2)
console.log(chars.includes("Y", -2)); // true — "Y" ที่ index 3
// fromIndex = -1 → เริ่มที่ index 4 (ตัวสุดท้าย — "W")
console.log(chars.includes("Y", -1)); // false — จาก index 4 ไม่มี "Y"
// fromIndex = -100 → น้อยเกินไป → เริ่มที่ 0
console.log(chars.includes("Y", -100)); // true — มี "Y" ตั้งแต่ index 0 (index 1)
console.log(chars.includes("X", -100)); // true — มี "X" ที่ index 0กฎ fromIndex สำหรับ includes() (เหมือนกับ indexOf): • fromIndex >= length → return false ทันที (ไม่ค้นเลย) • fromIndex < 0 → ใช้ length + fromIndex เป็นจุดเริ่ม ถ้าผลลัพธ์ < 0 → เริ่มที่ 0 • fromIndex >= 0 → เริ่มค้นตรง index นั้นเลย • ทิศทางค้นหายังเป็นซ้ายไปขวาในทุกกรณี ข้อแตกต่างที่ควรรู้: fromIndex ของ includes() ไม่ได้เปลี่ยนคำตอบจาก true/false เป็น index — มันแค่เปลี่ยนช่วงที่ค้นหา
จุดที่มือใหม่มักพลาด
- includes() return boolean ไม่ใช่ index — มือใหม่ที่คุ้นกับ indexOf อาจเผลอเขียน `const index = arr.includes(x)` แล้วคาดหวังจะได้เลข index — แต่ includes คืน true/false เท่านั้น
- includes() เจอ NaN (ต่างจาก indexOf) — แต่มือใหม่บางคนอาจไม่รู้และยังใช้ indexOf เช็ค NaN อยู่ ทำให้พลาด bug ที่เงียบเพราะ indexOf(NaN) = -1 เสมอ
- includes() ก็ case sensitive เหมือน indexOf — "Hello" กับ "hello" เป็นคนละตัว รวมถึง string method อย่าง startsWith/endsWith ก็ case sensitive เช่นกัน
- includes() ก็ type sensitive เหมือน indexOf — 1 กับ "1" เป็นคนละตัว ต้องใช้ type เดียวกันถึงจะเจอ
- includes() หา object/array ไม่ได้ด้วยค่าข้างใน — [{}].includes({}) → false (SameValueZero ก็ยังเทียบ reference) — ต้องใช้ find()
- fromIndex >= length → false ทันที — `arr.includes(x, arr.length)` = false เสมอ — อย่าเผลอใช้ arr.length เป็น fromIndex ถ้าต้องการตรวจสอบตั้งแต่เริ่มต้น
- ใช้ includes() กับ string — string มี includes() เช่นกัน แต่ใช้ตรวจสอบ substring ไม่ใช่ element — 'hello'.includes('hell') → true (string method คนละตัวกับ array method)