تغییرناپذیری یک اصل اصلی در برنامه نویسی تابعی است و چیزهای زیادی برای ارائه به برنامه های شیءگرا نیز دارد. یک شیء قابل تغییر، شیئی است که وضعیت آن پس از ایجاد قابل اصلاح است. یک شیء تغییرناپذیر، شیئی است که وضعیت آن پس از ایجاد قابل اصلاح نیست.
مثالی از یک شیء تغییرناپذیر در جاوااسکریپت چیست؟
در جاوااسکریپت، برخی از انواع داخلی (اعداد، رشتهها) تغییرناپذیر هستند، اما اشیاء سفارشی به طور کلی قابل تغییر هستند.
برخی از اشیاء داخلی تغییرناپذیر جاوااسکریپت عبارتند از Math، Date.
در اینجا چند روش برای افزودن/شبیهسازی تغییرناپذیری روی اشیاء ساده جاوااسکریپت آورده شده است.
ویژگیهای ثابت شیء
با ترکیب writable: false و configurable: false، میتوانید اساساً یک ثابت (قابل تغییر، بازتعریف یا حذف نیست) به عنوان یک ویژگی شیء ایجاد کنید، مانند:
let myObject = {};
Object.defineProperty(myObject, 'number', {
value: 42,
writable: false,
configurable: false,
});
console.log(myObject.number); // 42
myObject.number = 43;
console.log(myObject.number); // 42
جلوگیری از گسترش
اگر میخواهید از اضافه شدن ویژگیهای جدید به یک شیء جلوگیری کنید، اما بقیه ویژگیهای شیء را دست نخورده باقی بگذارید، Object.preventExtensions(...) را فراخوانی کنید:
var myObject = {
a: 2,
};
Object.preventExtensions(myObject);
myObject.b = 3;
myObject.b; // undefined
در حالت غیر-strict، ایجاد b بیصدا شکست میخورد. در حالت strict، یک TypeError پرتاب میکند.
مهر و موم کردن (Seal)
Object.seal() یک شیء 'مهر و موم شده' ایجاد میکند، به این معنی که یک شیء موجود را گرفته و اساساً Object.preventExtensions() را روی آن فراخوانی میکند، اما همچنین تمام ویژگیهای موجود آن را به عنوان configurable: false علامتگذاری میکند.
بنابراین، نه تنها نمیتوانید ویژگیهای بیشتری اضافه کنید، بلکه نمیتوانید هیچ ویژگی موجود را دوباره پیکربندی یا حذف کنید (اگرچه هنوز میتوانید مقادیر آنها را تغییر دهید).
منجمد کردن (Freeze)
Object.freeze() یک شیء منجمد ایجاد میکند، به این معنی که یک شیء موجود را گرفته و اساساً Object.seal() را روی آن فراخوانی میکند، اما همچنین تمام ویژگیهای 'دسترسیدهنده داده' را به عنوان writable:false علامتگذاری میکند، به طوری که مقادیر آنها قابل تغییر نباشند.
این رویکرد بالاترین سطح تغییرناپذیری است که میتوانید برای خود یک شیء به دست آورید، زیرا از هرگونه تغییر در شیء یا هر یک از ویژگیهای مستقیم آن جلوگیری میکند (اگرچه، همانطور که در بالا ذکر شد، محتویات هر شیء ارجاع شده دیگر تحت تأثیر قرار نمیگیرد).
var immutable = Object.freeze({});
منجمد کردن یک شیء اجازه نمیدهد ویژگیهای جدید به آن اضافه شود و از حذف یا تغییر ویژگیهای موجود جلوگیری میکند. Object.freeze() قابلیت شمارش، قابلیت پیکربندی، قابلیت نوشتن و پروتوتایپ شیء را حفظ میکند. شیء منتقل شده را برمیگرداند و یک کپی منجمد ایجاد نمیکند.
مزایا و معایب تغییرناپذیری چیست؟
مزایا
- تشخیص تغییر آسانتر - برابری شیء را میتوان به روشی کارآمد و آسان از طریق برابری ارجاعی تعیین کرد. این برای مقایسه تفاوتهای شیء در React و Redux مفید است.
- برنامههایی با اشیاء تغییرناپذیر کمتر پیچیدهتر هستند، زیرا نیازی به نگرانی در مورد چگونگی تکامل یک شیء در طول زمان ندارید.
- کپیهای دفاعی دیگر ضروری نیستند وقتی اشیاء تغییرناپذیر از توابع بازگردانده میشوند یا به آنها ارسال میشوند، زیرا هیچ امکانی برای تغییر یک شیء تغییرناپذیر توسط آن وجود ندارد.
- اشتراکگذاری آسان از طریق ارجاع - یک کپی از یک شیء به همان اندازه خوب است، بنابراین میتوانید اشیاء را کش کنید یا از همان شیء چندین بار استفاده مجدد کنید.
- Thread-safe - اشیاء تغییرناپذیر را میتوان با خیال راحت بین threads در یک محیط multi-threaded استفاده کرد زیرا هیچ خطری برای تغییر آنها در threadsهای همزمان در حال اجرا وجود ندارد.
- با استفاده از کتابخانههایی مانند ImmutableJS، اشیاء با استفاده از اشتراکگذاری ساختاری اصلاح میشوند و حافظه کمتری برای داشتن چندین شیء با ساختارهای مشابه مورد نیاز است.
معایب
- پیادهسازیهای ساده از ساختارهای داده تغییرناپذیر و عملیات آن میتواند منجر به عملکرد بسیار ضعیفی شود زیرا هر بار اشیاء جدیدی ایجاد میشوند. توصیه میشود برای ساختارهای داده تغییرناپذیر کارآمد و عملیاتی که از اشتراکگذاری ساختاری بهره میبرند، از کتابخانهها استفاده کنید.
- تخصیص (و آزاد کردن) بسیاری از اشیاء کوچک به جای اصلاح اشیاء موجود میتواند تأثیر عملکردی داشته باشد. پیچیدگی تخصیصدهنده یا جمعآوریکننده زباله معمولاً به تعداد اشیاء در heap بستگی دارد.
- ساختارهای داده چرخهای مانند نمودارها دشوار است. اگر دو شیء دارید که پس از مقداردهی اولیه قابل تغییر نیستند، چگونه میتوانید آنها را به یکدیگر ارجاع دهید؟
چگونه میتوانید در کد خود به تغییرناپذیری دست یابید؟
جایگزین استفاده از اعلانهای const همراه با تکنیکهای ذکر شده در بالا برای ایجاد است. برای 'تغییر' اشیاء، از عملگر spread، Object.assign، Array.concat() و غیره برای ایجاد اشیاء جدید به جای تغییر شیء اصلی استفاده کنید.
مثالها:
// مثال آرایه
const arr = [1, 2, 3];
const newArr = [...arr, 4]; // [1, 2, 3, 4]
// مثال شیء
const human = Object.freeze({ race: 'human' });
const john = { ...human, name: 'John' }; // {race: 'human', name: 'John'}
const alienJohn = { ...john, race: 'alien' }; // {race: 'alien', name: 'John'}