Kodokon kodokon.com

Proxy وReflect والبرمجة الفوقية

اعترض العمليات الأساسية للغة بواسطة مصائد Proxy وفوض السلوك الافتراضي بنظافة باستخدام Reflect.

10 دقيقة · 3 أسئلة

افتح هذا الدرس في Kodokon

يغلف Proxy كائنا هدفا ويعترض العمليات الداخلية للغة عبر ثلاث عشرة مصيدة: get، set، has (معامل indeleteProperty، ownKeys، apply، construct، وهكذا. يكشف كائن Reflect عن العمليات ذاتها بالضبط كدوال، بنفس التواقيع التي للمصائد. إنه أساس تفاعلية Vue 3، والنماذج الوهمية الديناميكية، وواجهات نمط ORM.

JAVASCRIPT
const target = { name: "Ada" };

const traced = new Proxy(target, {
  get(obj, prop, receiver) {
    console.log("get " + String(prop));
    return Reflect.get(obj, prop, receiver);
  }
});

traced.name; // get name
مصيدة get بسيطة مفوضة إلى Reflect.

لماذا Reflect.get(obj, prop, receiver) بدلا من obj[prop]؟ بسبب المتلقي (receiver). إذا كانت الخاصية موصلا (getter)، فإن Reflect.get يستدعيه مع this مشيرا إلى المتلقي - الوكيل أو كائن يرث منه - بينما obj[prop] سيضبط this على الهدف الخام. بدون المتلقي، تتعطل الموصلات التي تقرأ this حالما يرث كائن من الوكيل.

JAVASCRIPT
const base = {
  get label() {
    return this.name;
  }
};

const proxy = new Proxy(base, {
  get(target, prop, receiver) {
    return Reflect.get(target, prop, receiver);
  }
});

const child = Object.create(proxy);
child.name = "Ada";
console.log(child.label); // "Ada" thanks to the receiver
المتلقي يحفظ this في الموصلات الموروثة.

حالات الاستخدام الجادة: التحقق عند الكتابة، الخصائص الافتراضية المحسوبة أثناء التنفيذ، التسجيل، التحكم بالوصول (إخفاء المفاتيح المسبوقة بـ _ عبر has وownKeys)، أو الكائنات ذات السلوك الافتراضي. لاحظ أن Reflect.set تعيد قيمة منطقية بدلا من رمي استثناء في الوضع الصارم: يجب أن تنشر مصيدتك تلك القيمة المعادة.

JAVASCRIPT
function validated(target) {
  return new Proxy(target, {
    set(obj, prop, value, receiver) {
      if (prop === "age" && !Number.isInteger(value)) {
        throw new TypeError("age must be an integer");
      }
      return Reflect.set(obj, prop, value, receiver);
    }
  });
}

const user = validated({ age: 30 });
user.age = 31;       // ok
// user.age = "old"; // TypeError
تحقق شفاف عند الكتابة.

اختبار المعرفة

تأكّد من أنك تذكّرت النقاط الأساسية في هذا الدرس.

  1. في مصيدة get، لماذا نفضل Reflect.get(target, prop, receiver) على target[prop]؟
    • لأنها أسرع لأن Reflect يتجاوز النموذج الأولي
    • كي تعمل الموصلات مع this الصحيح (المتلقي) بدلا من الهدف الخام
    • لأن target[prop] سيعيد إطلاق المصيدة ويسبب تكرارا لا نهائيا
    • الشكلان متكافئان تماما
  2. أي مصيدة تعترض التعبير "id" in obj؟
    • get
    • has
    • ownKeys
    • getOwnPropertyDescriptor
  3. مصيدة get تعيد 42 لخاصية هدف قيمتها 1، معلنة غير قابلة للتهيئة وغير قابلة للكتابة. ماذا يحدث؟
    • يعيد الوكيل 42، فالمصيدة تأخذ الأولوية دائما
    • يعيد المحرك 1 بصمت
    • يرمي المحرك TypeError لانتهاك ثابت
    • يعتمد السلوك على محرك JavaScript