JavaScript
Array Methods
every
เรียนรู้ every() — method ตรวจสอบว่าทุก element ผ่านเงื่อนไข คืน boolean หยุดทันทีที่เจอ false ตัวแรก (short-circuit) ไม่เปลี่ยนต้นฉบับ ครอบคลุม callback, vacuous truth (empty array → true), De Morgan's law, every() vs some() ผ่าน Lab 4 ข้อ
`every()` คืออะไร — ตรวจสอบว่าทุก element ผ่านเงื่อนไข
`every()` เป็น method ของ array ที่ใช้ตรวจสอบว่า **ทุก element ใน array ผ่านเงื่อนไขที่กำหนดหรือไม่** วิธีทำงาน: • รับ callback function — เรียก callback ทีละ element ตามลำดับใน array • ถ้า callback return `true` ทุกตัว → `every()` คืน `true` • ถ้า callback return `false` แม้แต่ตัวเดียว → `every()` หยุดทันทีและคืน `false` • `every()` คืน **boolean** (`true` / `false`) — ไม่ใช่ element, index, หรือ array • `every()` ไม่เปลี่ยน array ต้นฉบับ (immutable method) ใช้ `every()` เมื่อคุณต้องการรู้ว่า "ทุกตัวผ่านหมดหรือไม่" — เช่น "สินค้าทุกตัวมีในสต็อกหรือไม่", "คะแนนทุกคนผ่านเกณฑ์หรือไม่", "ฟอร์มทุกช่องถูกกรอกครบหรือไม่"
const nums = [2, 4, 6, 8];
// every() ตรวจสอบว่าทุกตัวเป็นเลขคู่หรือไม่
const allEven = nums.every(n => n % 2 === 0);
console.log(allEven); // true ← ทุกตัวผ่าน ✓
console.log(nums); // [2, 4, 6, 8] ← ต้นฉบับไม่เปลี่ยน ✓
// === ถ้ามีตัวที่ไม่ผ่านเพียงตัวเดียว → false
const mixed = [2, 3, 4, 6];
console.log(mixed.every(n => n % 2 === 0)); // false — 3 ไม่เป็นเลขคู่!
สังเกตพฤติกรรม: • callback รับ element ทีละตัว — `n` คือ 2, 4, 6, 8 ตามลำดับ • ทุกตัว return `true` → `every()` คืน `true` • กับ `mixed`: 3 return `false` → `every()` หยุดที่ 3 และคืน `false` ทันที — 4 และ 6 ไม่ถูกเช็ก • ผลลัพธ์เป็น boolean (`true`/`false`) — ไม่ใช่ element ความแตกต่างกับ `some()`: • `every()` = "ทุกตัวผ่านไหม" → AND logic → หยุดที่ `false` ตัวแรก • `some()` = "มีตัวผ่านไหม" → OR logic → หยุดที่ `true` ตัวแรก
every() รับ element ทีละตัวตามลำดับ → ส่งให้ callback เช็ก true/false → ถ้า false ตัวแรก → หยุด! คืน false — ถ้าทุกตัวผ่าน → คืน true
`every()` หยุดที่ `false` ตัวแรก (short-circuit)
`every()` มีพฤติกรรม **short-circuit** — หยุดการทำงานทันทีที่เจอ element ตัวแรกที่ callback return `false` ข้อดีของ short-circuit: • **ประหยัดเวลา** — ไม่ต้องเช็ก element ที่เหลือเมื่อเจอตัวที่ไม่ผ่านแล้ว • **ปลอดภัย** — ถ้า callback มี side effect (เช่น console.log, HTTP request) — side effect จะไม่ถูกเรียกกับ element ที่เหลือ จำง่าย ๆ: `every()` = "ทุกคนต้องผ่าน" — เจอคนแรกที่ไม่ผ่าน → จบ! ไม่ต้องถามต่อ เปรียบเทียบกับ `some()`: • `every()` หยุดที่ `false` ตัวแรก — "เลิกเช็กเมื่อเจอคนที่ไม่ผ่าน" • `some()` หยุดที่ `true` ตัวแรก — "เลิกเช็กเมื่อเจอคนที่ผ่าน"
สิ่งสำคัญ: short-circuit ทำให้ `every()` เร็วกว่าการใช้ `for` loop ในกรณีที่ element ตัวแรก ๆ ไม่ผ่านเงื่อนไข — `every()` จะหยุดทันที ไม่ต้องวน loop ต่อจนครบ เมื่อไหร่ที่ short-circuit มีประโยชน์: • ตรวจสอบสินค้า 1000 รายการ — ถ้าตัวที่ 3 หมดสต็อก → หยุด ไม่ต้องเช็กอีก 997 ตัว • ตรวจสอบฟอร์ม 20 ช่อง — ถ้าช่องที่ 2 ว่าง → หยุด ไม่ต้องเช็กอีก 18 ช่อง
Callback ของ `every()` — element, index, array
callback function ที่ส่งให้ `every()` รับ parameter ได้ 3 ตัว — เหมือน `some()`, `map()`, `filter()`, `find()`, และ `findIndex()`: • **element** — ค่าของ element ปัจจุบัน (ใช้บ่อยที่สุด) • **index** — ตำแหน่ง index ของ element ปัจจุบัน (0, 1, 2, ...) • **array** — array ต้นฉบับทั้งหมด (ใช้น้อยมาก) กฎสำคัญของ callback ใน `every()`: • **ต้อง return boolean** — `true` = ผ่าน → ไปต่อ, `false` = ไม่ผ่าน → หยุด! • ค่าที่ return จะถูกแปลงเป็น boolean อัตโนมัติ (truthy/falsy) — เหมือน `some()` และ `filter()` • ถ้าลืม `return` ใน callback แบบ `{ }` → callback return `undefined` (falsy) → `every()` เห็น `false` ตัวแรก → คืน `false` ทันที!
const users = [
{ name: "สมชาย", active: true },
{ name: "สมหญิง", active: true },
{ name: "สมศรี", active: false },
{ name: "สมบัติ", active: true },
];
// ตรวจสอบว่าทุกคน active หรือไม่ — ใช้ index ดูว่าเช็กถึงตัวไหน
const allActive = users.every((u, i) => {
console.log("เช็ก index:", i, "→", u.name, "active:", u.active);
return u.active;
});
console.log("ทุกคน active:", allActive);
// เช็ก index: 0 → สมชาย active: true → true → ไปต่อ
// เช็ก index: 1 → สมหญิง active: true → true → ไปต่อ
// เช็ก index: 2 → สมศรี active: false → false → หยุด!
// ทุกคน active: false
// สมบัติ (index 3) ไม่ถูกเช็ก — every() หยุดที่สมศรี
`every()` ใช้ validate ได้ดีมาก — ถ้าทุกอย่างผ่าน → `true`, ถ้ามีจุดเดียวผิด → `false`: ```javascript // ตรวจสอบ form validation const fields = [ { name: "email", value: "user@test.com" }, { name: "password", value: "123456" }, { name: "username", value: "" }, // ← ว่าง! ไม่ผ่าน ]; const isFormValid = fields.every(f => f.value.length > 0); console.log(isFormValid); // false — username ว่าง! ``` **จำง่าย ๆ:** `every()` = "ทุกตัวต้องผ่าน" — ถ้ามีตัวเดียวไม่ผ่าน = `false` ทั้งหมด — เหมือนการตรวจข้อสอบที่ต้องผ่านทุกข้อถึงจะ "ผ่าน"
`every()` ในสถานการณ์จริง
`every()` เจอบ่อยมากในงานจริง — ทุกครั้งที่ต้อง validate ว่าทุกอย่างถูกต้อง: • ตรวจสอบว่าสินค้าทุกตัวมีในสต็อก • ตรวจสอบว่าผู้ใช้ทุกคน active • ตรวจสอบว่าฟอร์มทุกช่องถูกกรอกครบ • ตรวจสอบว่าคะแนนทุกคนผ่านเกณฑ์ • ตรวจสอบว่า element ทุกตัวตรงตามเงื่อนไข จุดเด่นของ `every()`: • คืน boolean → ใช้ใน `if` ได้ตรง • หยุดทันทีที่เจอ false → เร็วกว่าการวน loop เช็กทุกตัว • ชื่อ method สื่อความหมายชัด — "every" = "ทุกตัว" → อ่านแล้วเข้าใจเจตนาทันที ข้อสังเกต: งาน validate จริงมักอยากรู้ว่า "มีอะไรผิดปกติ" — ใช้ `!every()` = "มีบางตัวไม่ผ่าน" — ซึ่งเทียบเท่ากับ `some()` กับเงื่อนไขกลับด้าน
const products = [
{ name: "ปากกา", price: 25, inStock: true },
{ name: "สมุด", price: 45, inStock: true },
{ name: "ดินสอ", price: 10, inStock: true },
{ name: "ยางลบ", price: 15, inStock: false },
{ name: "ไม้บรรทัด", price: 30, inStock: true },
];
// ตรวจสอบ: สินค้าทุกตัวมีในสต็อกหรือไม่?
const allInStock = products.every(p => p.inStock);
console.log(allInStock); // false — ยางลบหมดสต็อก!
// ใช้ใน if — แสดงคำเตือนถ้ามีสินค้าหมด
if (!allInStock) {
console.log("⚠️ มีสินค้าบางตัวหมดสต็อก — ไม่สามารถดำเนินการต่อได้");
}
// เปรียบเทียบกับ filter() — filter() ต้องนับหรือเช็ก length
const outOfStock = products.filter(p => !p.inStock);
console.log(outOfStock.length === 0); // false — ได้ผลเหมือนกัน แต่ verbose กว่า
// every() เร็วกว่า — หยุดที่ "ยางลบ" (ตัวที่ 4) ไม่ต้องเช็ก "ไม้บรรทัด"
const users = [
{ name: "สมชาย", role: "viewer", active: true },
{ name: "สมหญิง", role: "editor", active: true },
{ name: "สมศรี", role: "admin", active: false },
{ name: "สมบัติ", role: "viewer", active: true },
];
// ตรวจสอบ: ผู้ใช้ทุกคน active หรือไม่?
const allActive = users.every(u => u.active);
console.log("ทุกคน active:", allActive); // false — สมศรี inactive
// ตรวจสอบ: ผู้ใช้ทุกคนมี role ที่ถูกต้อง?
const validRoles = ["viewer", "editor", "admin"];
const allValidRole = users.every(u => validRoles.includes(u.role));
console.log("ทุกคนมี role ถูกต้อง:", allValidRole); // true
// ตรวจสอบหลายเงื่อนไข — ทุกคน active และมี role ถูกต้อง (ใช้ &&)
const allValid = users.every(u => u.active && validRoles.includes(u.role));
console.log("ทุกคน active และ role ถูกต้อง:", allValid); // false
Empty array — `every()` คืน `true` เสมอ (vacuous truth)
นี่คือ **gotcha สำคัญที่สุดของ `every()`** — เมื่อเรียก `every()` กับ array ว่าง (`[]`) — `every()` จะคืน `true` เสมอ **ไม่ว่าเงื่อนไขใน callback จะเป็นอะไร** เพราะอะไร? หลักการ **vacuous truth** ในตรรกศาสตร์: "ทุก element ในเซตว่างผ่านเงื่อนไข X" — ประโยคนี้เป็นจริงเสมอ — เพราะไม่มี element ตัวไหนที่จะมาพิสูจน์ว่า"ไม่ผ่าน"ได้ นี่คือตรงข้ามกับ `some()`: • `[].every(fn)` → `true` (ไม่มีตัวไหนที่ไม่ผ่าน) • `[].some(fn)` → `false` (ไม่มีตัวไหนที่ผ่าน) ในทางปฏิบัติ — array ว่างอาจเกิดจาก: • ผลลัพธ์จาก `filter()` ที่กรองแล้วไม่มีตัวไหนผ่านเลย • API response ที่คืน array ว่าง • การ query ข้อมูลที่ไม่มีผลลัพธ์ ถ้าคุณใช้ `every()` โดยไม่เช็กก่อนว่า array มีข้อมูลหรือไม่ — คุณอาจได้ `true` ทั้งที่ array ว่างเปล่า!
// === empty array — every() vs some() — ระวังสับสน!
const empty = [];
// every() กับ empty array → true เสมอ (vacuous truth)
console.log(empty.every(n => n > 0)); // true — "ทุกตัวผ่าน" เพราะไม่มีตัวให้เช็ก
console.log(empty.every(n => n < 0)); // true — เช่นกัน!
// ไม่ว่าเงื่อนไขเป็นอะไร — every() คืน true สำหรับ empty array
// some() กับ empty array → false เสมอ
console.log(empty.some(n => n > 0)); // false — "ไม่มีตัวไหนผ่าน" เพราะไม่มีตัวให้เช็ก
// === ในทางปฏิบัติ — ระวังเมื่อใช้กับ array ที่อาจเป็น empty
function areAllPositive(arr) {
return arr.every(n => n > 0);
}
console.log(areAllPositive([])); // true — vacuous truth! อาจไม่ใช่สิ่งที่คุณต้องการ
console.log(areAllPositive([1, 2])); // true
console.log(areAllPositive([-1, 2])); // false
// ✅ ถ้าอยากให้ empty array = false — เช็ก length ก่อน
function areAllPositiveSafe(arr) {
return arr.length > 0 && arr.every(n => n > 0);
}
console.log(areAllPositiveSafe([])); // false ✓ — ปลอดภัยกว่า
วิธีป้องกัน vacuous truth: ```javascript // ✅ Pattern 1 — เช็ก length ก่อนใช้ every() function areAllValid(items) { return items.length > 0 && items.every(item => item.valid); } // ✅ Pattern 2 — แจ้งเตือนเมื่อ array ว่าง function validateItems(items) { if (items.length === 0) { console.warn("ไม่มีข้อมูลให้ตรวจสอบ"); return false; } return items.every(item => item.valid); } ``` **จำเลย:** `every()` คืน `true` สำหรับ array ว่าง — เช็ก `length > 0` ก่อนเสมอเมื่อ array อาจว่างได้
ข้อผิดพลาดที่พบบ่อยเมื่อใช้ `every()`
- สับสน `every()` กับ `some()` — `every()` = ทุกตัวต้องผ่าน → AND — `some()` = มีอย่างน้อย 1 ตัว → OR — `[1, 2, 3].every(n => n > 2)` → false (1 ไม่ > 2) — `[1, 2, 3].some(n => n > 2)` → true (3 > 2)
- ไม่ระวัง empty array — `[].every(n => n > 0)` → `true` — เพราะไม่มี element ไหนที่ "ไม่ผ่าน" (vacuous truth) — ตรงข้ามกับ `some()` ที่คืน `false` สำหรับ empty array — **gotcha สำคัญ!**
- คิดว่า `every()` คืน element — `every()` คืน **boolean** — ต่างจาก `find()` ที่คืน element — ห้ามใช้ `users.every(...).name` — ใช้ `find()` หรือ `filter()` ถ้าอยากได้ element
- ลืม `return` ใน callback — `arr.every(n => { n > 0 })` — ไม่มี return → callback return `undefined` (falsy) → `every()` เห็น false ตัวแรก → คืน `false` ทันที — แก้โดยใช้ shorthand `n => n > 0` หรือเติม `return`
- หยุดเช็กผิดทาง — `every()` หยุดเมื่อเจอ **false** — ไม่ใช่ true — ตรงข้ามกับ `some()` — `[2, 4, 5, 6].every(n => n % 2 === 0)` → หยุดที่ 5 (false ตัวแรก) → คืน false
const nums = [2, 4, 6, 8];
// ❌ ผิด — ใช้ every() แต่เข้าใจผิดว่าเป็น some()
// [2, 4, 6, 8].every(n => n > 3) → false — เพราะ 2 ไม่ > 3
// [2, 4, 6, 8].some(n => n > 3) → true — เพราะ 4, 6, 8 > 3
const allGreaterThan3 = nums.every(n => n > 3);
console.log("ทุกตัว > 3:", allGreaterThan3); // false — 2 ไม่ > 3
// ✅ ถูก — ใช้ every() เมื่ออยากรู้ว่าทุกตัว > 0 (เงื่อนไขที่ทุกตัวผ่าน)
const allPositive = nums.every(n => n > 0);
console.log("ทุกตัว > 0:", allPositive); // true ✓
// ✅ ถูก — ใช้ some() เมื่ออยากรู้ว่ามีบางตัว > 5
const someGreaterThan5 = nums.some(n => n > 5);
console.log("มีบางตัว > 5:", someGreaterThan5); // true — 6, 8 > 5
`every()` vs `some()` — เลือกใช้ให้เหมาะ
`every()` และ `some()` เป็นพี่น้องกัน — ทั้งคู่รับ callback และคืน boolean — แต่ทำงานในทิศทางตรงข้าม: • **`every()`** = **AND** — "ทุกตัวผ่านไหม" → หยุดที่ `false` ตัวแรก • **`some()`** = **OR** — "มีบางตัวผ่านไหม" → หยุดที่ `true` ตัวแรก ทั้งสอง method มีความสัมพันธ์ทางตรรกศาสตร์ผ่าน **De Morgan's law**: ``` !arr.every(fn) ≡ arr.some(x => !fn(x)) !arr.some(fn) ≡ arr.every(x => !fn(x)) ``` นั่นคือ: • "ไม่จริงที่ทุกตัวผ่าน" = "มีอย่างน้อย 1 ตัวที่ไม่ผ่าน" • "ไม่มีตัวไหนผ่านเลย" = "ทุกตัวไม่ผ่าน" กฎเลือกใช้ในงานจริง: • อยาก validate ว่าทุกอย่างถูกต้อง → `every()` • อยากตรวจสอบว่ามีอะไรผิดปกติ → `some()` กับเงื่อนไขกลับด้าน • `!every()` = มีบางตัวไม่ผ่าน → `!arr.every(fn)` เท่ากับ `arr.some(x => !fn(x))`
| เรื่อง | every() | some() |
|---|---|---|
| Logic | AND — ทุกตัวต้องผ่าน | OR — อย่างน้อย 1 ตัวผ่าน |
| คืน `true` เมื่อ | ทุกตัวผ่านเงื่อนไข | มีอย่างน้อย 1 ตัวผ่าน |
| คืน `false` เมื่อ | มีอย่างน้อย 1 ตัวไม่ผ่าน | ไม่มีตัวไหนผ่านเลย |
| หยุดเมื่อเจอ | `false` ตัวแรก | `true` ตัวแรก |
| Empty array | `true` (vacuous truth) | `false` |
| ใช้เมื่อ | Validate "ทุกอย่างถูกต้องไหม" | ตรวจสอบ "มีอะไรผิดปกติไหม" |
const nums = [2, 4, 6, 8, 9];
// === every() — ทุกตัวเป็นเลขคู่หรือไม่ → false (9 เป็นเลขคี่)
const allEven = nums.every(n => n % 2 === 0);
console.log("ทุกตัวเป็นเลขคู่:", allEven); // false
// === some() — มีเลขคี่หรือไม่ → true (9 เป็นเลขคี่)
const hasOdd = nums.some(n => n % 2 !== 0);
console.log("มีเลขคี่:", hasOdd); // true
// === De Morgan: !every(fn) ≡ some(!fn)
console.log(!nums.every(n => n % 2 === 0)); // true
console.log(nums.some(n => n % 2 !== 0)); // true — ได้ผลเหมือนกัน!
// === ตัวอย่างกับ user
const users = [
{ name: "สมชาย", active: true },
{ name: "สมหญิง", active: true },
{ name: "สมศรี", active: false },
];
// every() — ทุกคน active ไหม → false
console.log("ทุกคน active:", users.every(u => u.active)); // false
// some() — มีใคร inactive ไหม → true
console.log("มีคน inactive:", users.some(u => !u.active)); // true
// ทั้งคู่ตรวจสอบสิ่งเดียวกัน แต่ถามคนละคำถาม
// !every(active) ≡ some(!active) → true
console.log(!users.every(u => u.active) === users.some(u => !u.active)); // true
เคล็ดลับ: • "ทุกคนผ่านไหม" → `every()` — validate ทั้งหมด — หยุดที่ "คนแรกที่ไม่ผ่าน" • "มีใครไม่ผ่านไหม" → `some()` กับเงื่อนไขกลับด้าน — ตรวจสอบความผิดปกติ — หยุดที่ "คนแรกที่มีปัญหา" • ใช้ `every()` เมื่อต้องการให้ทุกอย่างถูกก่อนดำเนินการต่อ • ใช้ `some()` เมื่อต้องการแค่รู้ว่ามีปัญหาเกิดขึ้นหรือไม่ • **ระวัง empty array เสมอ** — `every()` คืน `true`, `some()` คืน `false` — ถ้า array อาจว่างได้ ให้เช็ก `arr.length > 0` ก่อน • `!every()` และ `some()` กับเงื่อนไขกลับด้านให้ผลลัพธ์เหมือนกัน — เลือกแบบที่อ่านแล้วเข้าใจเจตนาชัดกว่า
ทดลองใช้ `every()` ฟรี — ไม่มีถูกผิด
เช็กความเข้าใจก่อนลงมือทำ
ทดสอบความเข้าใจ concepts สำคัญของ every() ก่อนเริ่มทำ Lab — ตอบทุกข้อให้ครบเพื่อให้แน่ใจว่าพร้อมลงมือเขียนโค้ดจริง คำถามครอบคลุม 4 เรื่องที่ควรแม่น: ค่าที่ every() คืน, พฤติกรรมกับ empty array (vacuous truth), ข้อผิดพลาดจาก callback, และ De Morgan's law
ตอบคำถาม 4 ข้อให้ครบก่อนเริ่มเขียนโค้ดใน Lab
const allActive = users.every(u => { u.active });ตอบครบทุกข้อแล้ว — ถึงเวลาเอา concepts ไปใช้ใน Lab จริง 4 ข้อด้านล่าง: • **Lab 1** — ตรวจสอบตัวเลขด้วย `every()` • **Lab 2** — ใช้ `every()` กับ array ของ object (สินค้า) • **Lab 3** — `every()` vs `some()` + De Morgan's law • **Lab 4** — จัดการ empty array และ vacuous truth