ویژگیهای پنهان هر Property: آشنایی با Property Descriptors
مقدمه
در طول این دوره، Property ها را همیشه بهشکلی ساده تعریف کردهایم: یک نام و یک مقدار. اما در پسزمینه، هر Property در جاوااسکریپت، فراتر از مقدار خودش، مجموعهای از تنظیمات پنهان دارد که رفتار آن را کنترل میکند؛ مثلاً اینکه آیا میتوان مقدار آن را تغییر داد، یا آیا در حلقهی for...in نمایش داده میشود یا نه. این تنظیمات را Property Descriptor مینامند.
Property Descriptor چیست؟
هر Property معمولی (Data Property) در جاوااسکریپت، علاوه بر value (مقدار)، سه ویژگی پنهان دیگر هم دارد:
- writable — آیا مقدار Property قابل تغییر است؟
- enumerable — آیا Property در حلقههایی مثل
for...inیاObject.keysنمایش داده میشود؟ - configurable — آیا میتوان این Property را حذف کرد یا تنظیمات آن را دوباره تغییر داد؟
وقتی یک Property را به روش معمولی تعریف میکنیم، جاوااسکریپت بهطور پیشفرض هر سه مورد بالا را true قرار میدهد.
مشاهدهی Descriptor یک Property
تحلیل کد
خروجی این کد شیئی است شامل چهار Property: value: "علی"، writable: true، enumerable: true و configurable: true. این نشان میدهد که وقتی name را بهروش معمولی تعریف کردیم، جاوااسکریپت بهطور خودکار هر سه ویژگی کنترلی را فعال (true) در نظر گرفته است؛ یعنی این Property هم قابل تغییر، هم قابل شمارش، و هم قابل حذف یا بازپیکربندی است.
تعریف یا تغییر Descriptor با Object.defineProperty
برای تنظیم دستی این ویژگیها، از Object.defineProperty استفاده میکنیم:
تحلیل رفتار جاوااسکریپت
با اینکه config.apiVersion = "2.0" بدون هیچ خطای ظاهری اجرا میشود (در حالت غیر-Strict Mode)، مقدار آن هرگز واقعاً تغییر نمیکند؛ همچنان "1.0" باقی میماند. دلیل این رفتار، writable: false است که صریحاً به جاوااسکریپت میگوید این Property را نباید بتوان بازنویسی کرد. در Strict Mode (که، همانطور که در فصل قبل دیدیم، بدنهی Class همیشه در آن اجرا میشود)، تلاش برای تغییر یک Property با writable: false، بهجای نادیده گرفته شدن بیسروصدا، خطای TypeError صادر میکند.
enumerable — کنترل نمایش در حلقهها
در فصل حلقهها و همچنین فصل قبل (هنگام بررسی متدهای Class)، به مفهوم Enumerable اشاره کردیم؛ اکنون میتوانیم مستقیماً آن را کنترل کنیم:
تحلیل کد
با اینکه internalId واقعاً روی شیء user وجود دارد و مقدار آن (user.internalId) بهدرستی قابل خواندن است، اما نه در خروجی Object.keys ظاهر میشود و نه در حلقهی for...in پیمایش میشود؛ فقط name در هر دو نمایش داده میشود. این دقیقاً همان مکانیزمی است که در درس قبل دیدیم چرا متدهای داخل Class، برخلاف متدهایی که دستی روی prototype اضافه میشوند، توسط for...in پیمایش نمیشوند: جاوااسکریپت بهطور خودکار آنها را با enumerable: false میسازد.
configurable — قفل کردن ساختار یک Property
وقتی configurable: false باشد، دو کار دیگر هم ممکن نخواهد بود: حذف Property با delete، و تغییر دوبارهی تنظیمات آن (بهجز writable که در برخی شرایط خاص همچنان قابل تغییر به false است):
تحلیل رفتار جاوااسکریپت
تلاش برای حذف id با delete بیاثر میماند (در غیر-Strict Mode) و مقدار settings.id همچنان 42 باقی میماند؛ چون configurable: false صریحاً از حذف این Property جلوگیری میکند.
مقایسهی سه ویژگی در یک نگاه
| ویژگی | وقتی false باشد |
|---|---|
| writable | مقدار Property دیگر قابل تغییر نیست |
| enumerable | Property در for...in و Object.keys ظاهر نمیشود |
| configurable | Property دیگر قابل حذف یا بازپیکربندی نیست |
دریافت تمام Descriptor های یک شیء
برای بررسی همزمان تمام Property های یک شیء، میتوان از Object.getOwnPropertyDescriptors (با s در انتها) استفاده کرد:
let user = { name: "علی", age: 25 };
console.log(Object.getOwnPropertyDescriptors(user));
جمعبندی
هر Property در جاوااسکریپت، فراتر از مقدار خودش، دارای سه ویژگی کنترلی پنهان است: writable که قابلیت تغییر مقدار را تعیین میکند، enumerable که قابلیت پیمایش در حلقههایی مثل for...in را تعیین میکند، و configurable که قابلیت حذف یا بازپیکربندی Property را تعیین میکند. بهطور پیشفرض، Property هایی که به روش معمولی تعریف میشوند، هر سه ویژگی را true دارند؛ اما با Object.defineProperty میتوان هرکدام را بهطور دقیق کنترل کرد.
