เปรียบเทียบค่าแบบหลวมๆ กับแบบเข้มงวด
เวลาเราเขียนโปรแกรม เราต้องมีการเปรียบเทียบค่าอยู่เสมอ เช่น เช็กว่าผู้ใช้กรอกรหัสผ่านตรงกับที่ตั้งไว้ไหม มือใหม่มักจะใช้เครื่องหมาย == (Double Equals) เพราะดูใช้ง่ายและคุ้นตา แต่นี่คือกับดักที่ทำให้เกิดบั๊ก (Bug - ข้อผิดพลาดในโปรแกรม) ได้ง่ายที่สุด เพราะมันเป็นการเปรียบเทียบแบบหลวมๆ ที่ยอมให้ค่าคนละชนิดเท่ากันได้
ลองจินตนาการว่าเราถามเพื่อนว่า 0 กับ "0" เหมือนกันไหม ในชีวิตจริงเราคงบอกว่าไม่เหมือน เพราะอันหนึ่งคือตัวเลข อีกอันคือข้อความ แต่ใน JavaScript ถ้าเราใช้ == มันจะพยายามแปลงค่าให้เป็นชนิดเดียวกันก่อนเปรียบเทียบ ทำให้ผลลัพธ์ออกมาเป็นจริง ซึ่งมักไม่ใช่สิ่งที่เราต้องการในงานเขียนโค้ดจริง
วิธีแก้ที่ดีที่สุดคือการใช้ === (Triple Equals) หรือการเปรียบเทียบแบบเข้มงวดเสมอ วิธีนี้จะบังคับให้คอมพิวเตอร์เช็กทั้งค่าและชนิดของข้อมูล ถ้าชนิดไม่ตรงกัน มันจะตอบว่าไม่เท่ากันทันทีโดยไม่พยายามแปลงค่าให้เรา ทำให้โปรแกรมของเราทำงานได้แม่นยำและคาดเดาผลลัพธ์ได้ง่ายขึ้น
// แบบที่ผิด: การเปรียบเทียบแบบหลวม
console.log(0 == "0"); // ผลลัพธ์คือ true เพราะแปลงเลข 0 เป็นข้อความก่อนเทียบ
// แบบที่ถูก: การเปรียบเทียบแบบเข้มงวด
console.log(0 === "0"); // ผลลัพธ์คือ false เพราะชนิดข้อมูลไม่เหมือนกัน
ในตัวอย่างแรก บรรทัดแรกใช้ == ซึ่งทำให้ JavaScript แปลงเลข 0 ให้เป็นข้อความ "0" ก่อนเทียบ ทำให้ค่าเท่ากัน ส่วนบรรทัดที่สองใช้ === เพื่อเช็กทั้งค่าและประเภทข้อมูล ซึ่งเลข 0 เป็นประเภทตัวเลข (Number) ส่วน "0" เป็นข้อความ (String) ผลลัพธ์จึงออกมาเป็นเท็จอย่างที่ควรจะเป็น
ความสับสนระหว่าง var, let และ const
มือใหม่มักจะใช้ var ในการประกาศตัวแปร (Variable - ที่เก็บข้อมูลชั่วคราว) อยู่เสมอ เพราะเห็นคนอื่นใช้ในบทเรียนเก่าๆ แต่ในปัจจุบัน var ถือเป็นวิธีที่ไม่แนะนำแล้ว เพราะมันมีปัญหาเรื่องขอบเขตการใช้งานที่กว้างเกินไปจนคุมได้ยาก และอาจถูกเขียนทับค่าเดิมโดยไม่ตั้งใจ
ลองนึกภาพว่า var เหมือนกับกระดานไวท์บอร์ดกลางห้องที่ใครจะเดินมาลบหรือเขียนทับเมื่อไหร่ก็ได้ แต่ let และ const เหมือนกับสมุดโน้ตส่วนตัวที่ปลอดภัยกว่า let ใช้สำหรับค่าที่เปลี่ยนแปลงได้ในภายหลัง ส่วน const ใช้สำหรับค่าที่ประกาศแล้วจะไม่เปลี่ยนอีกเลยตลอดการทำงาน
การฝึกนิสัยใช้ const ให้มากที่สุดและใช้ let เมื่อจำเป็น จะช่วยให้โค้ดของคุณอ่านง่ายขึ้นมาก เพราะเพื่อนร่วมงานหรือตัวคุณเองในอนาคตจะรู้ทันทีว่าตัวแปรไหนห้ามเปลี่ยนค่า การเลิกใช้ var จะช่วยลดปัญหาบั๊กที่เกิดจากการประกาศตัวแปรซ้ำซ้อนโดยไม่รู้ตัวได้ดีที่สุด
// การใช้ var ที่อาจเกิดปัญหา
var name = "Alice";
var name = "Bob"; // ไม่มีข้อผิดพลาดแจ้งเตือน ค่าโดนทับเฉยๆ
// การใช้ let และ const ที่ปลอดภัยกว่า
const age = 25; // ค่านี้คงที่ตลอดไป
let score = 0; // ค่านี้เปลี่ยนได้ในอนาคต
บรรทัดที่ใช้ var เราสามารถประกาศชื่อตัวแปรซ้ำได้โดยโปรแกรมไม่เตือนอะไรเลย ซึ่งเป็นเรื่องอันตรายมาก ส่วน const จะล็อกค่าไว้ไม่ให้เปลี่ยน และ let จะยอมให้เปลี่ยนค่าได้แต่ห้ามประกาศชื่อซ้ำในบล็อกเดียวกัน ผลลัพธ์คือโปรแกรมจะฟ้องทันทีหากเราทำผิดกฎ ช่วยให้เราแก้บั๊กได้ตั้งแต่ตอนเขียน
การใช้งาน async และ await ให้ถูกจังหวะ
การดึงข้อมูลจากอินเทอร์เน็ตผ่าน API (ช่องทางเชื่อมต่อข้อมูลระหว่างโปรแกรม) เป็นเรื่องปกติของการเขียนเว็บ แต่สิ่งที่มือใหม่มักพลาดคือการลืมรอข้อมูลให้มาถึงก่อนแล้วนำไปใช้งานต่อ ทำให้ได้ผลลัพธ์เป็นค่าว่างหรือค่าที่ยังไม่พร้อมใช้งาน
เปรียบเทียบง่ายๆ เหมือนการสั่งอาหาร ถ้าคุณเดินไปสั่งแล้วหันหลังกลับทันที คุณจะไม่ได้อาหาร แต่ถ้าคุณใช้ await (คำสั่งหยุดรอจนกว่างานจะเสร็จ) คุณจะยืนรอจนกว่าพนักงานจะทำอาหารเสร็จและยื่นให้คุณถึงมือ การเขียนโปรแกรมก็เช่นกัน เราต้องรอให้ข้อมูลจากเซิร์ฟเวอร์ส่งกลับมาก่อนถึงจะเอาข้อมูลนั้นไปโชว์บนหน้าจอได้
หัวใจสำคัญคือต้องใส่คำว่า async ไว้หน้าฟังก์ชัน (Function - ชุดคำสั่งที่รวมกันไว้ทำงานเฉพาะอย่าง) เพื่อบอกให้โปรแกรมรู้ว่าข้างในนี้มีการทำงานที่ต้องรอเวลา และใช้ await นำหน้าคำสั่งดึงข้อมูลเสมอ วิธีนี้จะทำให้โค้ดของคุณทำงานตามลำดับที่ถูกต้องและไม่เกิดข้อผิดพลาดตอนนำข้อมูลไปแสดงผล
// แบบที่ผิด: ไม่รอข้อมูล
function getData() {
const data = fetch("https://api.example.com");
console.log(data); // จะได้แค่สถานะ Promise (สัญญาว่าจะส่งข้อมูลมา) ไม่ใช่ตัวข้อมูลจริง
}
// แบบที่ถูก: ใช้ await เพื่อรอข้อมูล
async function getData() {
const response = await fetch("https://api.example.com");
const data = await response.json();
console.log(data); // ได้ข้อมูลจริงออกมาแล้ว
}
ในแบบที่ผิด โปรแกรมจะข้ามไปบรรทัด console.log ทันทีทั้งที่ข้อมูลยังโหลดไม่เสร็จ ส่วนแบบที่ถูก เราใช้ await เพื่อหยุดบรรทัดนั้นไว้จนกว่า fetch จะโหลดข้อมูลเสร็จสมบูรณ์ ผลลัพธ์คือเราจะได้ข้อมูลในรูปแบบ JSON (รูปแบบการแลกเปลี่ยนข้อมูล) มาใช้งานจริงได้ทันที
ความเข้าใจเรื่อง Hoisting ที่มักทำให้สับสน
Hoisting (การยกประกาศขึ้นไปบนสุด) คือพฤติกรรมแปลกๆ ของ JavaScript ที่มักจะย้ายการประกาศตัวแปรขึ้นไปไว้บนสุดของขอบเขตการทำงานโดยอัตโนมัติ มือใหม่อาจจะงงว่าทำไมเรียกใช้ตัวแปรได้ทั้งที่ยังไม่ได้ประกาศบรรทัดก่อนหน้า ซึ่งความเข้าใจผิดตรงนี้อาจนำไปสู่บั๊กที่หาต้นตอได้ยาก
ลองนึกภาพว่ามีคนแอบหยิบชื่อคุณไปใส่ในรายชื่อผู้เข้าแข่งขันก่อนที่คุณจะเดินเข้างานจริงๆ ถ้าคุณใช้ var โปรแกรมจะอนุญาตให้คุณเรียกใช้ชื่อนั้นได้ก่อนประกาศจริง แต่ค่าจะเป็น undefined (ค่าที่ยังไม่ได้กำหนด) ซึ่งสร้างความสับสนว่าตกลงตัวแปรมีค่าหรือไม่มีกันแน่
วิธีป้องกันที่ดีที่สุดคือการประกาศตัวแปรทุกครั้งก่อนใช้งานเสมอ และเปลี่ยนไปใช้ let กับ const เพราะพวกมันไม่มีพฤติกรรม Hoisting แบบที่คาดเดาไม่ได้ หากคุณพยายามเรียกใช้ตัวแปรพวกนี้ก่อนประกาศ โปรแกรมจะแจ้งเตือนข้อผิดพลาดทันที ซึ่งเป็นเรื่องดีเพราะช่วยให้เรารู้ตัวและรีบแก้ไขก่อนที่โปรแกรมจะพัง
// ปัญหาจาก var
console.log(a); // ได้ค่า undefined
var a = 5;
// ความปลอดภัยจาก let
console.log(b); // เกิดข้อผิดพลาดทันที (ดีแล้ว!)
let b = 5;
บรรทัดแรกที่ใช้ var จะไม่แจ้งเตือนข้อผิดพลาดแต่คืนค่าเป็น undefined ซึ่งทำให้เราเผลอใช้ค่าว่างไปคำนวณ ส่วนบรรทัดที่ใช้ let จะหยุดการทำงานและแจ้งเตือนว่าเราพยายามเรียกใช้ตัวแปรก่อนประกาศ ทำให้เราเห็นข้อผิดพลาดชัดเจนและแก้ไขโค้ดให้ถูกต้องได้ทันที
การคัดลอกข้อมูลแบบผิดวิธี
ใน JavaScript เวลาเราคัดลอก Array (รายการข้อมูล) หรือ Object (กลุ่มข้อมูล) มือใหม่มักจะใช้เครื่องหมายเท่ากับธรรมดา ซึ่งจริงๆ แล้วมันไม่ใช่การคัดลอกค่า แต่เป็นการอ้างอิงถึงที่อยู่ของข้อมูลเดิม ถ้าเราไปแก้ไขข้อมูลในตัวแปรใหม่ ข้อมูลตัวเดิมก็จะเปลี่ยนตามไปด้วยโดยไม่รู้ตัว
เปรียบเทียบเหมือนคุณมีไฟล์เอกสารสำคัญหนึ่งฉบับ ถ้าคุณส่งลิงก์ไฟล์นั้นให้เพื่อน แล้วเพื่อนเข้าไปแก้เนื้อหาในไฟล์นั้น ไฟล์ต้นฉบับของคุณก็จะเปลี่ยนไปด้วย นี่คือสิ่งที่เรียกว่าการอ้างอิงตำแหน่งหน่วยความจำ (Reference) ซึ่งไม่ใช่สิ่งที่เราต้องการเมื่อเราต้องการทำสำเนาข้อมูลไว้ใช้งานแยกต่างหาก
วิธีแก้ไขที่ถูกต้องคือการใช้ Spread Operator (เครื่องหมายจุดสามจุด ...) เพื่อกระจายข้อมูลออกมาสร้างเป็นก้อนใหม่ วิธีนี้จะทำให้คุณได้สำเนาข้อมูลที่แยกออกจากของเดิมโดยเด็ดขาด ทำให้คุณสามารถแก้ไขข้อมูลใหม่ได้โดยไม่กระทบกับข้อมูลต้นฉบับ ซึ่งเป็นเทคนิคที่โปรแกรมเมอร์มืออาชีพใช้กันเป็นมาตรฐาน
// แบบที่ผิด: การอ้างอิงที่อยู่เดิม
const original = [1, 2, 3];
const copy = original;
copy.push(4);
console.log(original); // ค่าเดิมเปลี่ยนไปด้วย เป็น [1, 2, 3, 4]
// แบบที่ถูก: การสร้างสำเนาใหม่
const properCopy = [...original];
properCopy.push(5);
console.log(original); // ค่าเดิมยังคงเป็น [1, 2, 3, 4]
บรรทัดที่ใช้ copy = original เป็นเพียงการชี้ไปที่ข้อมูลเดียวกัน การแก้ไขตัวหนึ่งจึงกระทบอีกตัว ส่วน [...original] คือการแกะค่าออกมาใส่ในกล่องใบใหม่ที่แยกจากกันโดยสิ้นเชิง ผลลัพธ์คือ original จะไม่ถูกแก้ไขเมื่อเราเพิ่มข้อมูลเข้าไปใน properCopy
สรุปบทเรียนเพื่อการเป็นโปรแกรมเมอร์ที่ดี
การเรียนรู้ JavaScript ไม่ใช่แค่การจำคำสั่งให้ได้ แต่คือการเข้าใจพฤติกรรมของภาษาเพื่อลดความผิดพลาดในการทำงานจริง จากทั้ง 5 ข้อที่กล่าวมา ไม่ว่าจะเป็นการเปรียบเทียบค่า การจัดการตัวแปร หรือการคัดลอกข้อมูล หากคุณฝึกฝนจนเป็นนิสัยตั้งแต่วันนี้ คุณจะเขียนโค้ดที่สะอาดและมีบั๊กน้อยลงอย่างแน่นอน
ลองนำแนวทางเหล่านี้ไปใช้กับโปรเจกต์ (Project - งานที่สร้างขึ้นเพื่อฝึกฝน) เล็กๆ ของคุณดู เช่น การทำแอปรายการสิ่งที่ต้องทำ (To-do List) แทนที่จะใช้ var ให้ลองใช้ const และ let ให้ถูกที่ หรือลองใช้ === แทน == ทุกครั้งที่เขียนเงื่อนไขตรวจสอบค่า
จำไว้ว่าโปรแกรมเมอร์ที่เก่งไม่ได้หมายถึงคนที่ไม่เคยทำพลาด แต่คือคนที่เรียนรู้จากข้อผิดพลาดเหล่านั้นและปรับปรุงวิธีเขียนให้ดีขึ้นเสมอ หากคุณติดปัญหาในจุดไหน อย่าเพิ่งท้อ ให้ค่อยๆ อ่านข้อความแจ้งเตือนจากโปรแกรม แล้วปรับแก้ทีละจุด คุณจะเก่งขึ้นอย่างแน่นอนในทุกบรรทัดที่พิมพ์ครับ
ที่มา: 5 Common JavaScript Mistakes Beginners Make (And How to Fix Them) — DEV Community