Computer >> บทช่วยสอนคอมพิวเตอร์ >  >> การเขียนโปรแกรม >> JavaScript

สัญลักษณ์ (Symbols) ใน JavaScript - ชนิดข้อมูล Primitive ที่ไม่ซ้ำกัน

Symbol เป็นชนิดข้อมูล Primitive (Primitive Type) ใหม่ที่เพิ่มเข้ามาใน ES2015 (ES6) มีลักษณะเฉพาะคือ ค่าทุกตัวไม่ซ้ำกันทุกครั้งที่สร้าง แม้จะใช้คำอธิบาย (Description) เดียวกันก็ตาม

ลักษณะสำคัญของ Symbol

  • สร้างด้วย Symbol() (ไม่ใช่ Constructor ไม่มี new)
  • ค่าแต่ละตัว Unique ไม่ซ้ำกันแน่นอน
  • ใช้เป็น Key ของ Object Property ได้ (Hidden from normal iteration)
  • ไม่สามารถแปลงเป็น String ได้โดยอัตโนมัติ (ต้องใช้ .toString())

ตัวอย่าง: สร้างและเปรียบเทียบ Symbol

<!DOCTYPE html>
<html lang="th">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>JavaScript Symbols</title>
    <style>
        body { font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif; }
        .result { font-size: 18px; font-weight: 500; color: blueviolet; }
        button { padding: 8px 16px; margin: 10px 0; cursor: pointer; }
    </style>
</head>
<body>
    <h1>สัญลักษณ์ (Symbols) ใน JavaScript</h1>
    <div class="result"></div>
    <button class="Btn">แสดงและเปรียบเทียบ Symbols</button>
    <h3>กดปุ่มด้านบนเพื่อดู Symbols ใน Console และเปรียบเทียบ</h3>

    <script>
        let BtnEle = document.querySelector(".Btn");
        let resEle = document.querySelector(".result");
        
        // สร้าง Symbol สองตัวด้วยคำอธิบายเดียวกัน
        let sym1 = Symbol("test");
        let sym2 = Symbol("test");
        
        BtnEle.addEventListener("click", () => {
            console.log("sym1:", sym1);           // Symbol(test)
            console.log("sym2:", sym2);           // Symbol(test)
            console.log("sym1 === sym2:", sym1 === sym2); // false
            console.log("sym1.toString():", sym1.toString()); // "Symbol(test)"
            console.log("typeof sym1:", typeof sym1); // "symbol"
            
            if (sym1 !== sym2) {
                resEle.innerHTML = "❌ Symbols ไม่เท่ากัน (Unique ทุกตัว)";
            } else {
                resEle.innerHTML = "✅ Symbols เท่ากัน";
            }
        });
    </script>
</body>
</html>

ผลลัพธ์

หน้าจอ: ❌ Symbols ไม่เท่ากัน (Unique ทุกตัว)
Console: แสดง Symbol(test) ทั้งคู่ แต่ sym1 === sym2 เป็น false

การใช้งานจริงของ Symbol

1. ใช้เป็น Property Key ที่ "ซ่อน" (Non-enumerable)

const ID = Symbol("id");
const user = {
    name: "Somchai",
    [ID]: 12345  // ใช้ Computed Property Name
};

console.log(user.name);        // "Somchai"
console.log(user[ID]);         // 12345
console.log(Object.keys(user)); // ["name"] - Symbol ไม่อยู่!
console.log(Object.getOwnPropertySymbols(user)); // [Symbol(id)]

2. Well-known Symbols (Built-in Symbols)

// Symbol.iterator - ทำให้ Object เป็น Iterable
const myIterable = {
    items: [1, 2, 3],
    [Symbol.iterator]() {
        let index = 0;
        return {
            next: () => index < this.items.length 
                ? { value: this.items[index++], done: false }
                : { done: true }
        };
    }
};

for (let item of myIterable) {
    console.log(item); // 1, 2, 3
}

// Symbol.toStringTag - ปรับพฤติกรรม Object.prototype.toString
class MyClass {
    get [Symbol.toStringTag]() {
        return "MyClass";
    }
}
console.log(Object.prototype.toString.call(new MyClass())); 
// "[object MyClass]" แทนที่จะเป็น "[object Object]"

3. Global Symbol Registry (Symbol.for)

// Symbol.for() - ค้นหา/สร้างใน Global Registry
let symA = Symbol.for("app.user.id");
let symB = Symbol.for("app.user.id");
console.log(symA === symB); // true - ตัวเดียวกัน!

// Symbol.keyFor() - หาคำอธิบายจาก Global Symbol
console.log(Symbol.keyFor(symA)); // "app.user.id"

สรุป: เมื่อไหร่ควรใช้ Symbol

  • ต้องการ Property Key ที่ไม่ชนกับ Key อื่น (แม้จะเพิ่ม Library ภายนอก)
  • ต้องการ "Hidden" Property ที่ไม่แสดงใน for...in, Object.keys(), JSON.stringify()
  • Implement Protocol เช่น Iterator, Async Iterator, toStringTag
  • สร้าง Unique Constant สำหรับ Action Types, Event Names ฯลฯ