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 ฯลฯ