Symbol: هفتمین نوع داده‌ی primitive جاوااسکریپت

در فصل ۱۹ با Symbol.iterator به‌عنوان یک نمونه‌ی خاص از Symbol آشنا شدید. در این درس، خود نوع داده‌ی Symbol به‌طور مستقل بررسی می‌شود: نوعی primitive که ویژگی منحصربه‌فردی دارد—هر مقدار Symbol، حتی با توضیح یکسان، همیشه کاملاً یکتاست. این ویژگی، Symbol را ابزاری مناسب برای ساخت کلیدهای شیئی بدون خطر تداخل نام می‌کند.

ساخت یک Symbol

Symbol با فراخوانی تابع Symbol() ساخته می‌شود؛ این تابع بر‌خلاف String یا Number، هرگز با new فراخوانی نمی‌شود:

const sym1 = Symbol();
const sym2 = Symbol("توضیح اختیاری");

console.log(typeof sym1); // "symbol"
console.log(sym2.toString()); // "Symbol(توضیح اختیاری)"

پارامتر ورودی Symbol() صرفاً یک توضیح (description) برای اهداف دیباگ کردن است و هیچ نقشی در یکتا بودن مقدار Symbol ندارد.

یکتایی: مهم‌ترین ویژگی Symbol

حتی اگر دو Symbol با توضیح کاملاً یکسان ساخته شوند، آن‌ها همیشه دو مقدار کاملاً متفاوت و غیرمساوی هستند:

یکتایی مقادیر Symbol
خروجی کنسول اینجا نمایش داده می‌شود...

همین یکتایی تضمین‌شده است که Symbol را از یک رشته‌ی ساده کاملاً متمایز می‌کند؛ دو رشته با محتوای یکسان همیشه با هم برابرند، اما دو Symbol هرگز، حتی با توضیح یکسان.

کاربرد رایج: کلید شیئی بدون خطر تداخل

از آنجا که هر Symbol یکتاست، می‌توان از آن به‌عنوان کلید یک ویژگی در شیء استفاده کرد، بدون نگرانی از این‌که این کلید به‌طور تصادفی با ویژگی دیگری از همان شیء یا حتی کدی که بعداً به آن شیء اضافه می‌شود، تداخل پیدا کند:

const ID = Symbol("id");

const user = {
  name: "سارا",
  [ID]: 1024, // کلید Symbol باید داخل براکت نوشته شود
};

console.log(user[ID]); // 1024
console.log(user.name); // "سارا"

ویژگی‌های Symbol در حلقه‌ها و متدهای پیمایش نادیده گرفته می‌شوند

یکی از کاربردهای عملی این رفتار، پنهان نگه‌داشتن یک ویژگی از پیمایش‌های معمول است. ویژگی‌هایی که کلید آن‌ها Symbol است، به‌طور پیش‌فرض در for...in، Object.keys و JSON.stringify ظاهر نمی‌شوند:

const ID = Symbol("id");

const user = {
  name: "سارا",
  [ID]: 1024,
};

console.log(Object.keys(user));        // ["name"]؛ ID دیده نمی‌شود
console.log(JSON.stringify(user));      // '{"name":"سارا"}'؛ ID حذف شده

for (const key in user) {
  console.log(key); // فقط "name" چاپ می‌شود
}

برای دسترسی صریح به ویژگی‌های Symbol یک شیء، متد اختصاصی Object.getOwnPropertySymbols(user) استفاده می‌شود که آرایه‌ای از تمام کلیدهای Symbol آن شیء را برمی‌گرداند.

Symbol.for: ساخت Symbol مشترک و قابل بازیابی

گاهی نیاز است چند بخش مستقل از برنامه، دقیقاً به یک Symbol واحد دسترسی داشته باشند. برای این منظور، Symbol.for(key) استفاده می‌شود که برخلاف Symbol()، یک رجیستری سراسری دارد؛ فراخوانی آن با کلید یکسان، همیشه همان Symbol قبلی را برمی‌گرداند:

const symbolA = Symbol.for("shared-key");
const symbolB = Symbol.for("shared-key");

console.log(symbolA === symbolB); // true؛ برخلاف Symbol() معمولی

جدول مقایسه‌ی Symbol() و Symbol.for()

ویژگی Symbol() Symbol.for()
یکتایی همیشه یک مقدار کاملاً جدید و یکتا مقدار مشترک برای کلید یکسان
ثبت در رجیستری سراسری خیر بله
کاربرد رایج کلید خصوصی و محلی در یک ماژول اشتراک‌گذاری یک Symbol میان چند بخش مستقل برنامه

نکات کلیدی

  • Symbol یک نوع داده‌ی primitive است که هر مقدار آن، حتی با توضیح یکسان، همیشه کاملاً یکتاست.
  • Symbol معمولاً به‌عنوان کلید ویژگی‌های شیء استفاده می‌شود تا خطر تداخل نام با ویژگی‌های دیگر از بین برود.
  • ویژگی‌هایی با کلید Symbol، به‌طور پیش‌فرض در for...in، Object.keys و JSON.stringify نادیده گرفته می‌شوند.
  • Symbol.for برخلاف Symbol() معمولی، از یک رجیستری سراسری استفاده می‌کند و امکان اشتراک‌گذاری یک Symbol واحد میان بخش‌های مختلف برنامه را فراهم می‌کند.

جمع‌بندی

Symbol با تضمین یکتایی مطلق، ابزاری برای ساخت کلیدهای شیئی بدون ریسک تداخل نام و پنهان‌سازی ویژگی‌ها از پیمایش‌های معمول فراهم می‌کند. شناختی که در این درس از رفتار عمومی Symbol به دست آمد، مکمل مستقیم Well-Known Symbolهایی مثل Symbol.iterator است که در فصل ۱۹ با کاربرد تخصصی‌تر آن آشنا شدید.