الخالدة (Immutability) هي مبدأ أساسي في البرمجة الوظيفية، ولها الكثير لتقدمه للبرامج الموجهة للكائنات أيضًا. الكائن القابل للتعديل هو كائن يمكن تعديل حالته بعد إنشائه. الكائن غير القابل للتعديل هو كائن لا يمكن تعديل حالته بعد إنشائه.
ما هو مثال على كائن غير قابل للتعديل في JavaScript؟
في JavaScript، بعض الأنواع المدمجة (الأرقام، السلاسل) غير قابلة للتعديل، ولكن الكائنات المخصصة قابلة للتعديل بشكل عام.
بعض كائنات JavaScript المدمجة غير القابلة للتعديل هي Math
، Date
.
فيما يلي بعض الطرق لإضافة/محاكاة الخالدة على كائنات JavaScript العادية.
خصائص الكائن الثابتة
من خلال الجمع بين 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
في الوضع غير الصارم، يفشل إنشاء b
بصمت. في الوضع الصارم، يرمي TypeError
.
Seal
Object.seal()
ينشئ كائنًا "مختومًا"، مما يعني أنه يأخذ كائنًا موجودًا ويستدعي بشكل أساسي Object.preventExtensions()
عليه، ولكنه أيضًا يحدد جميع خصائصه الموجودة على أنها configurable: false
.
لذلك، لا يمكنك فقط إضافة المزيد من الخصائص، ولكن لا يمكنك أيضًا إعادة تكوين أو حذف أي خصائص موجودة (على الرغم من أنه لا يزال بإمكانك تعديل قيمها).
Freeze
Object.freeze()
ينشئ كائنًا مجمدًا، مما يعني أنه يأخذ كائنًا موجودًا ويستدعي بشكل أساسي Object.seal()
عليه، ولكنه أيضًا يحدد جميع خصائص "الوصول إلى البيانات" على أنها writable: false
، بحيث لا يمكن تغيير قيمها.
هذا النهج هو أعلى مستوى من الخالدة يمكنك تحقيقه لكائن نفسه، حيث يمنع أي تغييرات على الكائن أو على أي من خصائصه المباشرة (على الرغم من أن محتويات أي كائنات أخرى مشار إليها لا تتأثر، كما ذكر أعلاه).
var immutable = Object.freeze({});
تجميد الكائن لا يسمح بإضافة خصائص جديدة إلى الكائن ويمنع إزالة أو تغيير الخصائص الموجودة. يحافظ Object.freeze()
على قابلية التعداد، قابلية التكوين، قابلية الكتابة، ونموذج الكائن. يعيد الكائن الذي تم تمريره ولا ينشئ نسخة مجمدة.
ما هي إيجابيات وسلبيات الخالدة؟
الإيجابيات
- أسهل في اكتشاف التغييرات - يمكن تحديد مساواة الكائنات بطريقة فعالة وسهلة من خلال المساواة المرجعية. هذا مفيد لمقارنة اختلافات الكائنات في React و Redux.
- البرامج ذات الكائنات غير القابلة للتعديل أقل تعقيدًا في التفكير، حيث لا داعي للقلق بشأن كيفية تطور الكائن بمرور الوقت.
- لم تعد النسخ الدفاعية ضرورية عندما يتم إرجاع الكائنات غير القابلة للتعديل من الدوال أو تمريرها إليها، حيث لا يوجد احتمال لتعديل كائن غير قابل للتعديل بواسطتها.
- سهولة المشاركة عبر المراجع - نسخة واحدة من الكائن جيدة تمامًا مثل الأخرى، لذا يمكنك تخزين الكائنات مؤقتًا أو إعادة استخدام نفس الكائن عدة مرات.
- آمنة للاستخدام المتعدد الخيوط - يمكن استخدام الكائنات غير القابلة للتعديل بأمان بين الخيوط في بيئة متعددة الخيوط حيث لا يوجد خطر من تعديلها في خيوط أخرى تعمل بالتوازي.
- باستخدام مكتبات مثل ImmutableJS، يتم تعديل الكائنات باستخدام المشاركة الهيكلية ويحتاج إلى ذاكرة أقل لوجود كائنات متعددة ذات هياكل متشابهة.
السلبيات
- يمكن أن تؤدي التطبيقات البسيطة لهياكل البيانات غير القابلة للتعديل وعملياتها إلى أداء ضعيف للغاية لأنه يتم إنشاء كائنات جديدة في كل مرة. يوصى باستخدام المكتبات لهياكل البيانات غير القابلة للتعديل الفعالة والعمليات التي تستفيد من المشاركة الهيكلية.
- يمكن أن يتسبب تخصيص (وإلغاء تخصيص) العديد من الكائنات الصغيرة بدلاً من تعديل الكائنات الموجودة في تأثير على الأداء. تعتمد تعقيد المخصص أو جامع القمامة عادةً على عدد الكائنات في الكومة.
- من الصعب بناء هياكل البيانات الدورية مثل الرسوم البيانية. إذا كان لديك كائنان لا يمكن تعديلهما بعد التهيئة، فكيف يمكنك جعلهما يشيران إلى بعضهما البعض؟
كيف يمكنك تحقيق الخالدة في الكود الخاص بك؟
البديل هو استخدام تصريحات const
جنبًا إلى جنب مع التقنيات المذكورة أعلاه للإنشاء. لـ "تغيير" الكائنات، استخدم عامل الانتشار (spread operator)، 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"}