ภาพรวม (Overview)
บางทีเรามัวแต่ไป Optimize ที่ระดับ Framework (React/Vue) จนลืมไปว่า "พื้นฐาน" คือ JavaScript การเขียน JS ที่ดีช่วยให้เว็บเร็วขึ้นแบบไม่ต้องพึ่งท่าน่าพิสดาร วันนี้มาดูเทคนิค Micro-optimization ที่ Vercel Engineer แนะนำกันครับ
1. Index Maps: บอกลา O(N) ไปหา O(1)
ถ้าต้องเอาของใน Array A ไปหาใน Array B อย่าใช้ .find() ในลูปเด็ดขาด! เพราะมันจะกลายเป็น O(N²) (ช้ามากถ้าข้อมูลเยอะ)
❌ แบบช้า (O(N²)):
const orders = [
/* 1000 items */
];
const users = [
/* 1000 items */
];
// วนลูป order (1000 รอบ)
// ข้างในวนหา user (เฉลี่ย 500 รอบ)
// รวม = 500,000 operations!
const result = orders.map((order) => ({
...order,
user: users.find((u) => u.id === order.userId),
}));
✅ แบบเร็ว (O(N)): สร้าง Map ไว้ก่อน ค้นหาทีหลังจะเร็วระดับ O(1)
// สร้าง Map ทีเดียว (1000 operations)
const userMap = new Map(users.map((u) => [u.id, u]));
// วนลูป order (1000 รอบ) x หาใน map (1 operation)
// รวม = 2,000 operations (เร็วกว่า 250 เท่า!)
const result = orders.map((order) => ({
...order,
user: userMap.get(order.userId),
}));
2. Layout Thrashing: อย่าอ่านสลับเขียน
Browser จะทำงานหนักมากถ้าเรา "อ่านค่า Layout" (เช่น offsetWidth, clientHeight) สลับกับ "เขียนค่า Style" ไปมา เพราะทุกครั้งที่อ่าน Browser ต้องคำนวณ Layout ใหม่ทั้งหมด (Reflow)
❌ อ่านสลับเขียน (Force Reflow รัวๆ):
const box1 = document.getElementById("box1");
const box2 = document.getElementById("box2");
box1.style.width = "100px"; // เขียน
console.log(box1.offsetWidth); // อ่าน -> บังคับ Reflow ทันที!
box2.style.width = "100px"; // เขียน
console.log(box2.offsetWidth); // อ่าน -> บังคับ Reflow อีกรอบ!
✅ เขียนให้หมดก่อน ค่อยอ่านทีเดียว:
// เขียนรวดเดียว (Browser จะ batch การเปลี่ยน style ไว้)
box1.style.width = "100px";
box2.style.width = "100px";
// อ่านทีเดียว (Browser คำนวณ Reflow แค่ครั้งเดียว)
console.log(box1.offsetWidth);
console.log(box2.offsetWidth);
3. Cache Property Access
การเข้าถึงตัวแปรที่ซ้อนลึกๆ (obj.a.b.c.d) ใน Loop กินทรัพยากรมากกว่าที่คิด เพราะ JS Engine ต้องวิ่งตาม Chain ไปเรื่อยๆ
❌ วิ่งหาของทุกรอบ:
for (let i = 0; i < 10000; i++) {
// ต้องวิ่งหา config -> settings -> value หมื่นรอบ
process(app.config.settings.value);
}
✅ ดึงมาถือไว้ก่อน:
// วิ่งหาทีเดียว
const value = app.config.settings.value;
for (let i = 0; i < 10000; i++) {
process(value); // ใช้ตัวแปรโดยตรง เร็วกว่าเห็นๆ
}
4. Set/Map Lookups ดีกว่า Array.includes
ถ้าต้องเช็คว่า "มีของชิ้นนี้ไหม" ในรายการที่เยอะๆ ให้ใช้ Set แทน Array ครับ
❌ Array.includes (O(N)):
const blockedIds = [/* 10,000 IDs */];
// ต้องไล่เช็คทีละตัวจนกว่าจะเจอ
if (blockedIds.includes(currentId)) { ... }
✅ Set.has (O(1)):
const blockedSet = new Set(blockedIds);
// เช็คเปรี้ยงเดียวรู้เลย ไม่ว่าข้อมูลจะเยอะแค่ไหน
if (blockedSet.has(currentId)) { ... }
สรุป
Algorithm และ Data Structure พื้นฐานยังคงสำคัญที่สุดครับ
- ใช้
Map/Setเมื่อต้องค้นหาข้อมูล - ระวังการอ่าน Layout (
offsetWidth, etc.) - Cache ตัวแปรใน Loop เสมอ
เขียน JS ให้ดี แล้ว Framework จะทำงานเบาลงครับ!