ใน
บทที่ ๓ ได้เขียนถึงข้อมูลชนิดต่างๆที่มีตั้งแต่ ES3 ไปแล้วว่ามี ๕ ชนิด ตัวเลข (number), สายอักขระ (string), บูล (boolean), ออบเจ็กต์ (object), ฟังก์ชัน (function)
ใน ES6 มีชนิดข้อมูลเพิ่มมาอีกอย่างคือชนิดที่เรียกว่า
ซิมโบล (symbol)
นี่เป็นข้อมูลที่มีความเข้าใจยากหน่อย และอาจไม่ได้ใช้งานมากเท่าชนิดอื่น แต่ก็มีประโยชน์ในการใช้งานในบางกรณีจึงได้ถูกเพิ่มเข้ามา
ในบทนี้จะพูดถึงการสร้างและการใช้งานซิมโบล
การสร้างซิมโบล
ข้อมูลชนิดซิมโบลสามารถสร้างขึ้นได้โดยฟังก์ชัน Symbol
let sol = Symbol();
alert(typeof sol); // ได้ symbol
ซิมโบลจะไม่เปลี่ยนเป็นสายอักขระโดยอัตโนมัติ ดังนั้นถ้าใช้กับ alert หรือนำไปคำนวณกับข้อมูลชนิดอื่นจะเกิดข้อผิดพลาด แต่สามารถใช้เมธอด .toString เพื่อเปลี่ยนเป็นสายอักขระโดยตรงได้
alert(sol.toString()); // ได้ Symbol()
alert(sol); // ได้ TypeError: can't convert symbol to string
sol+1; // ได้ TypeError: can't convert symbol to number
Symbol ใช้เป็นฟังก์ชันสร้างข้อมูลโดยตรงเท่านั้น ใช้ new แบบคอนสตรักเตอร์ไม่ได้ ต่างจากพวก Number, String, Object ที่สามารถใช้ได้
let sol = new Symbol() // ได้ TypeError: Symbol is not a constructor
สามารถใส่คำอธิบายลงไปในวงเล็บตอนสร้างซิมโบลได้ คำที่ใส่ไปจะปรากฏตอนใช้ toString()
let sol = Symbol("ซิม")
alert(sol.toString()) // ได้ Symbol(ซิม)
คุณสมบัติของซิมโบล
คุณสมบัติที่สำคัญของซิมโบลก็คือไม่ว่าจะไปเปรียบเทียบด้วย == หรือ === กับอะไรก็จะได้ false หมด แม้แต่ซิมโบลที่ถูกสร้างให้เหมือนกันก็ตาม
แต่จะได้ true เมื่อเทียบกับตัวมันเองเท่านั้น หรือเป็นตัวแปรที่รับค่าจากซิมโบลตัวนั้นโดยตรง
let sol1 = Symbol("ซิม");
let sol2 = Symbol("ซิม");
alert(sol1==sol2); // ได้ false
alert(sol1===sol2); // ได้ false
alert(sol1=="sol1"); // ได้ false
alert(sol1==sol1); // ได้ true
alert(sol1===sol1); // ได้ true
let sol3 = sol1;
alert(sol1==sol3); // ได้ true
alert(sol1===sol3); // ได้ true
ในตัวอย่างนี้ sol1 และ sol2 ต่างก็คือ
Symbol("ซิม")
เหมือนกัน แต่พอมาเปรียบเทียบกันกลับได้ false นั่นเพราะซิมโบลแต่ละตัวจะถือว่าไม่ซ้ำกับใครนอกจากตัวมันเอง
แต่ sol3 ในที่นี้คือแทน sol1 โดยตรง ดังนั้นจึงถือเป็นซิมโบลตัวเดียวกัน เมื่อนำมาเปรียบเทียบจึงถือว่าเท่ากัน
คุณสมบัติตรงนี้คือสิ่งที่เป็นเอกลักษณ์ของซิมโบล ซึ่งจะถูกนำไปใช้ประโยชน์
การใช้ซิมโบลแทนค่าคงที่
สมมุติว่าเราต้องการค่าคงที่ที่แทนอะไรบางอย่างเพื่อแยกว่าเป็นของคนละชนิดกัน เพื่อใช้แยกกรณี หรือเป็นตัวเลือกอะไรบางอย่าง
โดยทั่วไปตัวอย่างที่เห็นมากก็คือใช้ตัวเลขแทนเพื่อแบ่งแยก เช่น
const MISAKA = 0;
const SHIRAI = 1;
const UIHARU = 2;
const SATEN = 3;
function f(k) {
if (k === MISAKA) {
// คำสั่งอะไรบางอย่าง
}
else if (k === SHIRAI) {
// คำสั่งอะไรบางอย่าง
}
else {
// คำสั่งอะไรบางอย่าง
}
}
let u = UIHARU, m = MISAKA;
f(m); // เข้าเงื่อนไข if แรก
f(SHIRAI); // เข้าเงื่อนไขที่ ๒
f(u); // เข้าเงื่อนไข else สุดท้าย
การใช้ตัวเลขแบบนี้ก็ใช้ได้ก็จริง แต่ว่าถ้าแบบนี้หมายความว่าไม่ต้องแทนด้วยตัวแปรค่าคงที่นั้น แต่ใส่ตัวเลข 0, 1, 2, ... ไปธรรมดาก็ใช้งานได้เหมือนกัน เพราะเนื้อในค่าในนี้จริงๆก็เป็นตัวเลข
let x = 0;
if (x === MISAKA) {
// คำสั่งอะไรบางอย่าง
}
แบบนี้ผลที่ได้ก็จะเป็นจริงแล้วเข้าเงื่อนไขได้เหมือนกัน
แม้ที่จริงตรงนี้ใช้ตัวเลขแทนไปเลยก็ไม่ใช่ปัญหาอะไรหนัก แต่เพื่อความอ่านง่ายจึงเก็บไว้ในข้อมูลที่เป็นตัวแปร จะง่ายกว่าต้องมาจำว่าเลขอะไรแทนอะไร อีกทั้งถ้าเผลอใช้เลขซ้ำกันขึ้นมาก็จะเกิดบั๊กได้ง่าย
อีกทั้งยังต้องกำหนดเป็นตัวเลขทั้งๆที่จริงๆเราแค่ต้องการตัวแปรที่แค่แยกแยะให้เห็นว่าเป็นข้อมูลคนละตัวกันเท่านั้นเอง ไม่จำเป็นต้องเป็นตัวเลข ขอแค่เป็นสิ่งที่ต่างกัน
ดังนั้นตรงนี้เราสามารถใช้ซิมโบลแทนได้ เพราะซิมโบลมีคุณสมบัติที่จะไม่ซ้ำกับตัวใดๆที่นอกเหนือจากมันเอง
const MISAKA = Symbol();
const SHIRAI = Symbol();
const UIHARU = Symbol();
const SATEN = Symbol();
let x = SATEN;
alert(x == SATEN); // ได้ true
alert(x == MISAKA); // ได้ false
แบบนี้จะแน่นอนได้ว่าค่าคงที่แต่ละตัวไม่มีใครมาซ้ำกับตัวนั้นเองหรือตัวแปรที่ใช้เท่ากับ = ป้อนให้ตัวนั้นโดยตรงเท่านั้น
ซิมโบลทุกตัวจะสร้างเป็น Symbol() เฉยๆโดยไม่ต้องใส่คำอธิบายในวงเล็บเลยเหมือนกันหมดเลยก็ไม่มีปัญหาอะไร เพราะยังไงก็ถือเป็นคนละตัวกันถ้าสร้างใหม่แยกกัน หรืออาจใส่คำอธิบายไปในวงเล็บด้วยก็ได้ เช่น
Symbol("นี่คือซิม")
แต่ก็ไม่ได้มีผลอะไร
การใช้ซิมโบลแทนคีย์ของออบเจ็กต์
ซิมโบลสามารถใช้เป็นคีย์ของออบเจ็กต์ได้ด้วย
ตัวอย่าง สร้างซิมโบลขึ้นมาแล้วใช้เป็นคีย์ในออบเจ็กต์โดยการใส่วงเล็บเหลี่ยม [ ] คร่อมชื่อตัวแปร
const INDEX = Symbol();
const TOUMA = Symbol();
let toaru = {
[INDEX]: 10000,
[TOUMA]: 100
};
alert(toaru.INDEX); // ได้ undefined
alert(toaru[INDEX]); // ได้ 10000
alert(toaru["INDEX"]); // ได้ undefined
alert(toaru[TOUMA]); // ได้ 100
alert(toaru[Symbol()]); // ได้ undefined
let i2 = INDEX;
alert(toaru[i2]); // ได้ 10000
จะเห็นว่าออบเจ็กต์นี้ใช้คีย์เป็นซิมโบลเหมือนกัน แต่ถือเป็นคีย์คนละตัวกัน และถึงสร้างซิมโบลใหม่ก็ไม่อาจมาใช้แทนได้ ที่จะใช้แทนได้ก็มีแต่ตัวแปรที่ตั้งค่าให้เป็นซิมโบลตัวนั้นเองเท่านั้น
ประโยชน์จากการใช้ซิมโบลเป็นชื่อพรอเพอร์ตีก็คือสามารถสร้างสิ่งที่เรียกว่า
พรอเพอร์ตีส่วนตัว (private property) และ
เมธอดส่วนตัว (private method) ให้กับออบเจ็กต์ได้
แต่วิธีนี้มักจะใช้เมื่อสร้างคลาสขึ้นมาในมอดูล ดังนั้นก็จะยกไปเขียนถึงใน
บทถัดไปซึ่งว่าด้วยเรื่องของการสร้างมอดูล