كما نعلم، يمكن للكائنات تخزين الخصائص.
حتى الآن، كانت الخاصية تمثل زوجًا بسيطًا من "القيمة الرئيسية" بالنسبة لنا. لكن خاصية الكائن هي في الواقع شيء أكثر مرونة وقوة.
سندرس في هذا الفصل خيارات التكوين الإضافية، وفي الفصل التالي سنرى كيفية تحويلها بشكل غير مرئي إلى وظائف getter/setter.
خصائص الكائن، إلى جانب value
، لها ثلاث سمات خاصة (ما يسمى "الأعلام"):
writable
- إذا كان true
، فيمكن تغيير القيمة، وإلا فستكون للقراءة فقط.
enumerable
- إذا كان true
، فسيتم إدراجه في حلقات، وإلا فلن يتم إدراجه.
configurable
- إذا كان true
، فيمكن حذف الخاصية وتعديل هذه السمات، وإلا فلا.
لم نرهم بعد، لأنهم بشكل عام لا يظهرون. عندما نقوم بإنشاء خاصية "بالطريقة المعتادة" فكلها true
. ولكن يمكننا أيضًا تغييرها في أي وقت.
أولاً، دعونا نرى كيفية الحصول على تلك الأعلام.
تسمح الطريقة Object.getOwnPropertyDescriptor بالاستعلام عن المعلومات الكاملة حول الممتلكات.
بناء الجملة هو:
دع واصف = Object.getOwnPropertyDescriptor(obj, propertyName);
obj
الكائن الذي يتم الحصول على المعلومات منه.
propertyName
اسم العقار.
القيمة التي تم إرجاعها هي ما يسمى بكائن "واصف الخاصية": فهو يحتوي على القيمة وجميع العلامات.
على سبيل المثال:
السماح للمستخدم = { الاسم: "جون" }; دع واصف = Object.getOwnPropertyDescriptor(user, 'name'); تنبيه (JSON.stringify (descriptor، null، 2 ) )؛ /* واصف الخاصية: { "القيمة": "جون"، "قابل للكتابة": صحيح، "لا تعد ولا تحصى": صحيح، "قابل للتكوين": صحيح } */
لتغيير العلامات، يمكننا استخدام Object.defineProperty.
بناء الجملة هو:
Object.defineProperty(obj، propertyName، واصف)
obj
، propertyName
الكائن وخاصيته لتطبيق الواصف.
descriptor
كائن واصف الخاصية المطلوب تطبيقه.
إذا كانت الخاصية موجودة، defineProperty
بتحديث علاماتها. وبخلاف ذلك، فإنه ينشئ الخاصية بالقيمة والأعلام المحددة؛ في هذه الحالة، إذا لم يتم توفير العلم، فسيتم افتراض أنه false
.
على سبيل المثال، هنا يتم إنشاء name
الخاصية مع جميع العلامات الزائفة:
دع المستخدم = {}؛ Object.defineProperty(user, "name", { القيمة: "جون" }); دع واصف = Object.getOwnPropertyDescriptor(user, 'name'); تنبيه (JSON.stringify (descriptor، null، 2 ) )؛ /* { "القيمة": "جون"، "قابل للكتابة": خطأ، "لا تعد ولا تحصى": كاذبة، "قابل للتكوين": خطأ } */
قارنه مع user.name
"الذي تم إنشاؤه بشكل طبيعي" أعلاه: الآن أصبحت جميع العلامات زائفة. إذا لم يكن هذا ما نريده، فمن الأفضل أن نضبطه على true
في descriptor
.
الآن دعونا نرى تأثيرات الأعلام بالقدوة.
لنجعل user.name
غير قابل للكتابة (لا يمكن إعادة تعيينه) عن طريق تغيير العلامة writable
:
السماح للمستخدم = { الاسم: "جون" }; Object.defineProperty(user, "name", { قابل للكتابة: كاذب }); user.name = "بيت"; // خطأ: لا يمكن تعيين خاصية "الاسم" للقراءة فقط
الآن لا يمكن لأحد تغيير اسم مستخدمنا، إلا إذا قاموا بتطبيق defineProperty
الخاصة بهم لتجاوز اسم المستخدم الخاص بنا.
تظهر الأخطاء فقط في الوضع الصارم
في الوضع غير الصارم، لا تحدث أخطاء عند الكتابة إلى خصائص غير قابلة للكتابة وما شابه. لكن العملية ما زالت لن تنجح. يتم تجاهل الإجراءات المخالفة للعلم بصمت في الحالات غير الصارمة.
إليك نفس المثال، ولكن تم إنشاء الخاصية من البداية:
دع المستخدم = { }; Object.defineProperty(user, "name", { القيمة: "جون"، // بالنسبة للخصائص الجديدة، نحتاج إلى إدراج ما هو صحيح بشكل صريح لا تعد: صحيح، شكلي: صحيح }); تنبيه (اسم المستخدم) ؛ // جون user.name = "بيت"; // خطأ
الآن دعونا نضيف toString
مخصصًا إلى user
.
عادةً، تكون toString
المضمنة للكائنات غير قابلة للتعداد، ولا تظهر في for..in
. لكن إذا أضفنا toString
خاصة بنا، فستظهر افتراضيًا في for..in
، على النحو التالي:
السماح للمستخدم = { الاسم: "جون"، إلى سلسلة () { إرجاع هذا. الاسم؛ } }; // بشكل افتراضي، يتم إدراج كلا الخاصيتين لدينا: لـ (السماح بإدخال المستخدم) تنبيه (مفتاح) ؛ // الاسم، toString
إذا لم يعجبنا ذلك، فيمكننا تعيين enumerable:false
. وبعد ذلك لن تظهر في حلقة for..in
، تمامًا مثل الحلقة المضمنة:
السماح للمستخدم = { الاسم: "جون"، إلى سلسلة () { إرجاع هذا. الاسم؛ } }; Object.defineProperty(user, "toString", { لا تعد: كاذبة }); // الآن تختفي سلسلة toString الخاصة بنا: لـ (السماح بإدخال المستخدم) تنبيه (مفتاح) ؛ // اسم
يتم أيضًا استبعاد الخصائص غير القابلة للإحصاء من Object.keys
:
تنبيه(Object.keys(user)); // اسم
يتم أحيانًا تعيين العلامة غير القابلة للتكوين ( configurable:false
) مسبقًا للكائنات والخصائص المضمنة.
لا يمكن حذف خاصية غير قابلة للتكوين، ولا يمكن تعديل سماتها.
على سبيل المثال، Math.PI
غير قابل للكتابة وغير قابل للإحصاء وغير قابل للتكوين:
دع واصف = Object.getOwnPropertyDescriptor(Math, 'PI'); تنبيه (JSON.stringify (descriptor، null، 2 ) )؛ /* { "القيمة": 3.141592653589793، "قابل للكتابة": خطأ، "لا تعد ولا تحصى": كاذبة، "قابل للتكوين": خطأ } */
لذلك، لا يستطيع المبرمج تغيير قيمة Math.PI
أو الكتابة فوقها.
Math.PI = 3; // خطأ لأنه قابل للكتابة: خطأ // حذف Math.PI لن يعمل أيضًا
لا يمكننا أيضًا تغيير Math.PI
ليصبح writable
مرة أخرى:
// خطأ بسبب قابل للتكوين: خطأ Object.defineProperty(Math, "PI", { writable: true });
لا يوجد شيء على الإطلاق يمكننا القيام به مع Math.PI
.
إن جعل خاصية غير قابلة للتكوين هو طريق ذو اتجاه واحد. لا يمكننا تغييره مرة أخرى باستخدام defineProperty
.
يرجى ملاحظة ما يلي: configurable: false
يمنع تغيير إشارات الخاصية وحذفها، مع السماح بتغيير قيمتها.
هنا user.name
غير قابل للتكوين، ولكن لا يزال بإمكاننا تغييره (لأنه قابل للكتابة):
السماح للمستخدم = { الاسم: "جون" }; Object.defineProperty(user, "name", { شكلي: كاذبة }); user.name = "بيت"; // يعمل بشكل جيد حذف اسم المستخدم؛ // خطأ
وهنا نجعل user.name
ثابتًا "مختومًا إلى الأبد"، تمامًا مثل Math.PI
المدمج:
السماح للمستخدم = { الاسم: "جون" }; Object.defineProperty(user, "name", { قابل للكتابة: كاذب، شكلي: كاذبة }); // لن يكون قادرًا على تغيير اسم المستخدم أو علاماته // كل هذا لن ينجح: user.name = "بيت"; حذف اسم المستخدم؛ Object.defineProperty(user, "name", { value: "Pete" });
تغيير السمة الوحيد الممكن: قابل للكتابة صحيح → خطأ
هناك استثناء بسيط حول تغيير الأعلام.
يمكننا تغيير writable: true
إلى false
بالنسبة لخاصية غير قابلة للتكوين، وبالتالي منع تعديل قيمتها (لإضافة طبقة أخرى من الحماية). ولكن ليس العكس.
هناك طريقة Object.defineProperties(obj, descriptors) التي تسمح بتعريف العديد من الخصائص في وقت واحد.
بناء الجملة هو:
Object.defineProperties(obj, { الدعامة 1: الواصف 1، الدعامة 2: الواصف 2 // ... });
على سبيل المثال:
Object.defineProperties(user, { الاسم: {القيمة: "جون"، قابلة للكتابة: خطأ }، اللقب: {القيمة: "سميث"، قابلة للكتابة: خطأ }، // ... });
لذلك، يمكننا تعيين العديد من الخصائص في وقت واحد.
للحصول على جميع واصفات الخصائص مرة واحدة، يمكننا استخدام الطريقة Object.getOwnPropertyDescriptors(obj).
يمكن استخدامه مع Object.defineProperties
كطريقة "مدركة للأعلام" لاستنساخ كائن:
Let clone = Object.defineProperties({}, Object.getOwnPropertyDescriptors(obj));
عادة عندما نقوم باستنساخ كائن، نستخدم مهمة لنسخ الخصائص، مثل هذا:
لـ (السماح بإدخال المستخدم) { استنساخ [مفتاح] = مستخدم [مفتاح] }
…ولكن هذا لا ينسخ الأعلام. لذا، إذا أردنا استنساخًا "أفضل"، فمن الأفضل استخدام Object.defineProperties
.
هناك اختلاف آخر وهو أن for..in
يتجاهل الخصائص الرمزية وغير القابلة للإحصاء، لكن Object.getOwnPropertyDescriptors
يُرجع جميع واصفات الخصائص بما في ذلك واصفات الخصائص الرمزية وغير القابلة للإحصاء.
تعمل واصفات الخصائص على مستوى الخصائص الفردية.
هناك أيضًا طرق تحد من الوصول إلى الكائن بأكمله :
Object.preventExtensions(obj)
يمنع إضافة خصائص جديدة إلى الكائن.
Object.seal(obj)
يمنع إضافة/إزالة الخصائص. مجموعات configurable: false
لجميع الخصائص الموجودة.
كائن.تجميد (كائن)
يمنع إضافة/إزالة/تغيير الخصائص. مجموعات configurable: false, writable: false
لجميع الخصائص الموجودة.
وأيضا هناك اختبارات لهم:
Object.isExtensible(obj)
يُرجع false
إذا كانت إضافة الخصائص محظورة، وإلا يكون true
.
Object.isSealed(obj)
يُعاد true
إذا كانت إضافة/إزالة الخصائص محظورة، وكانت جميع الخصائص الموجودة configurable: false
.
Object.isFrozen(obj)
يُرجع true
إذا كانت إضافة/إزالة/تغيير الخصائص محظورة، وكانت جميع الخصائص الحالية configurable: false, writable: false
.
ونادرا ما تستخدم هذه الأساليب في الممارسة العملية.