ไทย

เชี่ยวชาญตัวดำเนินการ nullish coalescing (??) ของ JavaScript เพื่อการกำหนดค่าเริ่มต้นที่สะอาดและมีประสิทธิภาพยิ่งขึ้น เรียนรู้ความแตกต่างจากตัวดำเนินการ OR (||) พร้อมดูตัวอย่างการใช้งานจริง

JavaScript Nullish Coalescing: คู่มือฉบับสมบูรณ์สำหรับการกำหนดค่าเริ่มต้น

ใน JavaScript การกำหนดค่าเริ่มต้นเป็นงานที่ทำกันโดยทั่วไป ตามปกติแล้ว นักพัฒนาจะใช้ตัวดำเนินการ OR (||) เพื่อจุดประสงค์นี้ อย่างไรก็ตาม ตัวดำเนินการ nullish coalescing (??) ซึ่งเปิดตัวใน ECMAScript 2020 ได้มอบวิธีการที่แม่นยำและเชื่อถือได้มากขึ้นในการจัดการกับการกำหนดค่าเริ่มต้น โดยเฉพาะอย่างยิ่งเมื่อต้องจัดการกับค่า null หรือ undefined คู่มือนี้จะเจาะลึกเกี่ยวกับตัวดำเนินการ nullish coalescing โดยสำรวจไวยากรณ์ พฤติกรรม ความแตกต่างจากตัวดำเนินการ OR และกรณีการใช้งานจริง

Nullish Coalescing คืออะไร?

ตัวดำเนินการ nullish coalescing (??) เป็นตัวดำเนินการทางตรรกะที่จะคืนค่าตัวถูกดำเนินการฝั่งขวา (right-hand side operand) เมื่อตัวถูกดำเนินการฝั่งซ้าย (left-hand side operand) เป็น null หรือ undefined มิฉะนั้นจะคืนค่าตัวถูกดำเนินการฝั่งซ้าย พูดง่ายๆ ก็คือ มันจะให้ค่าเริ่มต้นก็ต่อเมื่อตัวแปรเป็น null หรือ undefined อย่างเคร่งครัดเท่านั้น

ไวยากรณ์ (Syntax)

ไวยากรณ์สำหรับตัวดำเนินการ nullish coalescing นั้นตรงไปตรงมา:

leftOperand ?? rightOperand

ในที่นี้ leftOperand คือตัวแปรหรือนิพจน์ที่คุณต้องการตรวจสอบว่าเป็น null หรือ undefined หรือไม่ และ rightOperand คือค่าเริ่มต้นที่คุณต้องการกำหนดหาก leftOperand เป็น null หรือ undefined จริงๆ

ตัวอย่าง

พิจารณาตัวอย่างต่อไปนี้:

const username = null ?? "Guest";
console.log(username); // ผลลัพธ์: Guest

const age = undefined ?? 25;
console.log(age); // ผลลัพธ์: 25

const city = "London" ?? "Unknown";
console.log(city); // ผลลัพธ์: London

ในตัวอย่างนี้ username ถูกกำหนดค่าเริ่มต้นเป็น "Guest" เพราะค่าเริ่มต้นเป็น null ในทำนองเดียวกัน age ถูกกำหนดเป็น 25 เพราะเริ่มต้นเป็น undefined อย่างไรก็ตาม city ยังคงค่าเดิมคือ "London" เพราะมันไม่ใช่ทั้ง null และ undefined

ค่า Nullish กับค่า Falsy

การทำความเข้าใจความแตกต่างระหว่างค่า nullish และค่า falsy ใน JavaScript เป็นสิ่งสำคัญอย่างยิ่ง ค่า nullish คือค่าที่เป็น null หรือ undefined ส่วนค่า falsy คือค่าที่ถือว่าเป็นเท็จเมื่อพบในบริบทของบูลีน (Boolean) ค่า Falsy ประกอบด้วย:

ข้อแตกต่างที่สำคัญคือตัวดำเนินการ nullish coalescing จะตรวจสอบเฉพาะ null หรือ undefined เท่านั้น ในขณะที่ตัวดำเนินการ OR (||) จะตรวจสอบค่า falsy ใดๆ

ความแตกต่างระหว่าง ?? และ ||

ตัวดำเนินการ OR (||) เป็นตัวดำเนินการตรรกะ OR ที่จะคืนค่าตัวถูกดำเนินการฝั่งขวาหากตัวถูกดำเนินการฝั่งซ้ายเป็นค่า falsy แม้ว่าจะสามารถใช้สำหรับการกำหนดค่าเริ่มต้นได้ แต่ก็อาจนำไปสู่พฤติกรรมที่ไม่คาดคิดเมื่อต้องจัดการกับค่าเช่น 0 หรือสตริงว่าง

ตัวอย่าง: ข้อควรระวังของ ||

const quantity = 0 || 10; // เราตั้งใจให้มีค่าเริ่มต้นเป็น 10 หากไม่มี quantity
console.log(quantity); // ผลลัพธ์: 10 (ไม่คาดคิด!) เพราะ 0 เป็นค่า falsy

const text = '' || 'Default Text'; //เราตั้งใจให้มีข้อความเริ่มต้นหากไม่มี text
console.log(text); // ผลลัพธ์: Default Text (ไม่คาดคิด!) เพราะ '' เป็นค่า falsy

ในตัวอย่างแรก เราตั้งใจจะกำหนดค่าเริ่มต้นของ quantity เป็น 10 ก็ต่อเมื่อ quantity หายไป (null หรือ undefined) เท่านั้น อย่างไรก็ตาม เนื่องจาก 0 เป็นค่า falsy ตัวดำเนินการ OR จึงกำหนดค่าเริ่มต้นอย่างไม่ถูกต้อง ในทำนองเดียวกัน สตริงว่างทำให้ข้อความเริ่มต้นถูกแสดงแม้ว่าสตริงนั้นจะมีอยู่ (แต่เป็นสตริงว่าง) ก็ตาม

การใช้ ?? เพื่อความแม่นยำ

ลองเขียนตัวอย่างก่อนหน้านี้ใหม่โดยใช้ตัวดำเนินการ nullish coalescing:

const quantity = 0 ?? 10;
console.log(quantity); // ผลลัพธ์: 0 (ถูกต้อง!)

const text = '' ?? 'Default Text';
console.log(text); // ผลลัพธ์: '' (ถูกต้อง!)

ตอนนี้ ผลลัพธ์เป็นไปตามที่คาดไว้ ตัวดำเนินการ nullish coalescing จะตรวจสอบเฉพาะ null หรือ undefined เท่านั้น ดังนั้น 0 และ '' จึงถือเป็นค่าที่ถูกต้อง และค่าดั้งเดิมของมันจึงถูกคงไว้

กรณีการใช้งานสำหรับ Nullish Coalescing

ตัวดำเนินการ nullish coalescing มีประโยชน์ในสถานการณ์ต่างๆ ที่คุณต้องการให้ค่าเริ่มต้นก็ต่อเมื่อตัวแปรเป็น null หรือ undefined อย่างเคร่งครัดเท่านั้น นี่คือกรณีการใช้งานทั่วไปบางส่วน:

1. การจัดการพารามิเตอร์ของฟังก์ชันที่เป็นทางเลือก (Optional)

เมื่อกำหนดฟังก์ชันที่มีพารามิเตอร์ที่เป็นทางเลือก คุณสามารถใช้ตัวดำเนินการ nullish coalescing เพื่อกำหนดค่าเริ่มต้นหากไม่มีการส่งพารามิเตอร์เข้ามา

function greet(name, greeting) {
  const userName = name ?? "User";
  const userGreeting = greeting ?? "Hello";
  console.log(`${userGreeting}, ${userName}!`);
}

greet(); // ผลลัพธ์: Hello, User!
greet("Alice"); // ผลลัพธ์: Hello, Alice!
greet("Bob", "Greetings"); // ผลลัพธ์: Greetings, Bob!

2. การตั้งค่าตัวเลือกการกำหนดค่าเริ่มต้น

เมื่อทำงานกับอ็อบเจ็กต์การกำหนดค่า (configuration objects) คุณสามารถใช้ตัวดำเนินการ nullish coalescing เพื่อให้แน่ใจว่ามีการใช้ค่าเริ่มต้นหากตัวเลือกการกำหนดค่าบางอย่างไม่ได้ถูกระบุไว้

const config = {
  timeout: 5000,
  retries: 3
};

function fetchData(options) {
  const timeout = options.timeout ?? 10000; // ค่า timeout เริ่มต้น 10 วินาที
  const retries = options.retries ?? 5; // ค่า retries เริ่มต้น 5 ครั้ง
  console.log(`Timeout: ${timeout}, Retries: ${retries}`);
}

fetchData(config); // ผลลัพธ์: Timeout: 5000, Retries: 3
fetchData({}); // ผลลัพธ์: Timeout: 10000, Retries: 5
fetchData({timeout:null, retries: undefined}); // ผลลัพธ์: Timeout: 10000, Retries: 5

3. การเข้าถึงคุณสมบัติของอ็อบเจ็กต์ที่ซ้อนกัน

เมื่อเข้าถึงคุณสมบัติของอ็อบเจ็กต์ที่ซ้อนกัน สามารถใช้ตัวดำเนินการ nullish coalescing ร่วมกับ optional chaining (?.) เพื่อกำหนดค่าเริ่มต้นหากคุณสมบัติระดับกลางใดๆ เป็น null หรือ undefined

const user = {
  profile: {
    address: {
      city: "New York"
    }
  }
};

const cityName = user?.profile?.address?.city ?? "Unknown";
console.log(cityName); // ผลลัพธ์: New York

const unknownUser = {};
const unknownCityName = unknownUser?.profile?.address?.city ?? "Unknown";
console.log(unknownCityName); // ผลลัพธ์: Unknown

4. การทำงานกับ API และข้อมูลภายนอก

เมื่อดึงข้อมูลจาก API หรือแหล่งข้อมูลภายนอก สามารถใช้ตัวดำเนินการ nullish coalescing เพื่อกำหนดค่าเริ่มต้นหากฟิลด์ข้อมูลบางฟิลด์หายไปหรือมีค่าเป็น null หรือ undefined ลองพิจารณาการดึงข้อมูลผู้ใช้จากภูมิภาคต่างๆ สมมติว่าบางภูมิภาคอาจไม่รวมฟิลด์ `country` ในข้อมูลผู้ใช้ของพวกเขา

async function getUserData(userId) {
  try {
    const response = await fetch(`https://api.example.com/users/${userId}`);
    const data = await response.json();
    const country = data.country ?? "Unknown Country";
    const timezone = data.timezone ?? "UTC";
    console.log(`User is from: ${country}, Timezone: ${timezone}`);
  } catch (error) {
    console.error("Error fetching user data:", error);
  }
}

// จำลองการตอบสนอง API ที่แตกต่างกัน:
const userWithCountry = { name: "John", country: "USA", timezone: "EST" };
const userWithoutCountry = { name: "Jane", timezone: "GMT" };

// เพื่อทดสอบสิ่งนี้ คุณจะต้องมี API จริงหรือ mock fetch
// เพื่อวัตถุประสงค์ในการสาธิต ลองจำลองการตอบสนอง:
global.fetch = async (url) => {
    if (url.includes("123")) {
        return { json: async () => userWithCountry };
    } else if (url.includes("456")) {
        return { json: async () => userWithoutCountry };
    }
    throw new Error("Unexpected URL");
};

getUserData(123); // ผลลัพธ์: User is from: USA, Timezone: EST
getUserData(456); // ผลลัพธ์: User is from: Unknown Country, Timezone: GMT

ลำดับความสำคัญของตัวดำเนินการ (Operator Precedence)

ตัวดำเนินการ nullish coalescing มีลำดับความสำคัญของตัวดำเนินการที่ค่อนข้างต่ำ โดยต่ำกว่าตัวดำเนินการ OR (||) และ AND (&&) ดังนั้น เมื่อใช้ตัวดำเนินการ nullish coalescing ร่วมกับตัวดำเนินการทางตรรกะอื่นๆ จำเป็นต้องใช้วงเล็บเพื่อกำหนดลำดับการดำเนินการอย่างชัดเจน การไม่ทำเช่นนั้นอาจส่งผลให้เกิดข้อผิดพลาดทางไวยากรณ์ (syntax errors) หรือพฤติกรรมที่ไม่คาดคิด

ตัวอย่าง: การใช้วงเล็บเพื่อความชัดเจน

// ไม่มีวงเล็บ (SyntaxError)
// const result = false || null ?? "Default"; // SyntaxError: Unexpected token '??'

// มีวงเล็บ (ถูกต้อง)
const result = false || (null ?? "Default");
console.log(result); // ผลลัพธ์: Default

const anotherResult = (false || null) ?? "Default";
console.log(anotherResult); // ผลลัพธ์: null

ในตัวอย่างแรก การไม่ใส่วงเล็บส่งผลให้เกิด SyntaxError เนื่องจาก JavaScript engine ไม่สามารถระบุลำดับการดำเนินการที่ต้องการได้ การเพิ่มวงเล็บเป็นการบอก engine อย่างชัดเจนให้ประเมินตัวดำเนินการ nullish coalescing ก่อน ตัวอย่างที่สองนั้นถูกต้อง แต่ผลลัพธ์แตกต่างออกไปเนื่องจากนิพจน์ || ถูกประเมินก่อน

ความเข้ากันได้กับเบราว์เซอร์ (Browser Compatibility)

ตัวดำเนินการ nullish coalescing (??) เป็นฟีเจอร์ที่ค่อนข้างใหม่ ดังนั้นจึงเป็นเรื่องสำคัญที่ต้องพิจารณาความเข้ากันได้กับเบราว์เซอร์ โดยเฉพาะอย่างยิ่งหากคุณตั้งเป้าไปที่เบราว์เซอร์รุ่นเก่า เบราว์เซอร์สมัยใหม่ส่วนใหญ่รองรับตัวดำเนินการ nullish coalescing ได้แก่:

หากคุณต้องการรองรับเบราว์เซอร์รุ่นเก่า คุณสามารถใช้ transpiler เช่น Babel เพื่อแปลงโค้ดของคุณเป็น JavaScript เวอร์ชันที่เข้ากันได้ Babel จะแปลงตัวดำเนินการ ?? เป็นโค้ด JavaScript ที่เทียบเท่าซึ่งทำงานได้ในสภาพแวดล้อมที่เก่ากว่า

แนวทางปฏิบัติที่ดีที่สุด (Best Practices)

นี่คือแนวทางปฏิบัติที่ดีที่สุดบางประการสำหรับการใช้ตัวดำเนินการ nullish coalescing อย่างมีประสิทธิภาพ:

ข้อควรพิจารณาในระดับสากล (Global Considerations)

เมื่อพัฒนาสำหรับผู้ใช้ทั่วโลก ให้พิจารณาประเด็นต่อไปนี้ที่เกี่ยวข้องกับการกำหนดค่าเริ่มต้น:

ตัวอย่าง: การปรับให้เข้ากับท้องถิ่นด้วย Nullish Coalescing

สมมติว่าคุณต้องการแสดงข้อความต้อนรับเริ่มต้นในภาษาต่างๆ ตาม locale ของผู้ใช้ คุณสามารถใช้ตัวดำเนินการ nullish coalescing เพื่อให้ข้อความเริ่มต้นหากไม่มีข้อความที่แปลไว้

function getWelcomeMessage(locale) {
  const localizedMessages = {
    en: "Welcome!",
    fr: "Bienvenue !",
    de: "Willkommen!"
  };

  const message = localizedMessages[locale] ?? "Welcome!"; // ใช้ภาษาอังกฤษเป็นค่าเริ่มต้นหากไม่พบ locale
  return message;
}

console.log(getWelcomeMessage("fr")); // ผลลัพธ์: Bienvenue !
console.log(getWelcomeMessage("es")); // ผลลัพธ์: Welcome! (ใช้ค่าเริ่มต้นเป็นภาษาอังกฤษ)

บทสรุป

ตัวดำเนินการ nullish coalescing (??) เป็นส่วนเสริมที่มีค่าสำหรับภาษา JavaScript มันมอบวิธีการที่แม่นยำและเชื่อถือได้มากขึ้นในการกำหนดค่าเริ่มต้นเมื่อเทียบกับตัวดำเนินการ OR (||) โดยเฉพาะอย่างยิ่งเมื่อต้องจัดการกับค่าเช่น 0 หรือสตริงว่าง การทำความเข้าใจไวยากรณ์ พฤติกรรม และกรณีการใช้งาน จะช่วยให้คุณสามารถเขียนโค้ดที่สะอาดและบำรุงรักษาง่ายขึ้น ซึ่งจัดการกับการกำหนดค่าเริ่มต้นได้อย่างแม่นยำ อย่าลืมพิจารณาความเข้ากันได้กับเบราว์เซอร์ ลำดับความสำคัญของตัวดำเนินการ และข้อควรพิจารณาในระดับสากลเมื่อใช้ตัวดำเนินการ nullish coalescing ในโปรเจกต์ของคุณ

โดยการปฏิบัติตามแนวทางที่ดีที่สุดที่ระบุไว้ในคู่มือนี้ คุณจะสามารถใช้ประโยชน์จากตัวดำเนินการ nullish coalescing ได้อย่างมีประสิทธิภาพเพื่อปรับปรุงคุณภาพและความน่าเชื่อถือของโค้ด JavaScript ของคุณ ทำให้โค้ดมีความแข็งแกร่งและเข้าใจง่ายขึ้น โปรดจำไว้เสมอว่าต้องให้ความสำคัญกับความชัดเจนและการบำรุงรักษาในโค้ดของคุณ และตัวดำเนินการ nullish coalescing สามารถเป็นเครื่องมือที่ทรงพลังในการบรรลุเป้าหมายเหล่านี้ได้