٣ يوليو ٢٠٢٠

النوع المرجعي

خصائص متقدمه فى اللغه

هذه المقالة تقوم بتغطية موضوع متقدم, لفهم بعض الحالات بشكل أفضل.

إنها ليست مهمة. يعيش العديد من المطورين ذوي الخبرة بشكل جيد دون معرفة ذلك. تابع القراءة إذا كنت تريد معرفة كيفية عمل الأشياء خلف الكواليس.

قد تفقد استدعاء تابع تم تقييمه بشكل ديناميكي this.

علي سبيل المثال:

let user = {
  name: "John",
  hi() { alert(this.name); },
  bye() { alert("Bye"); }
};

user.hi(); // تعمل

// الآن دعونا نقوم بتشغيل user.hi أو user.bye بناءً علي الإسم
(user.name == "John" ? user.hi : user.bye)(); // Error!

على السطر الأخير يوجد عامل شرطي يختار إما user.hi أو user.bye. فى هذه الحالة تكون النتيجة user.hi.

ثم يتم استدعاء التابع على الفور بين قوسين (). و لكنه لم يعمل بشكل صحيح!

كما ترى, تشغيل التابع أحدث خطأ, بسبب أن نتيجة "this" داخل تشغيل التابع أنتج undefined.

هذا يعمل (كائن نقطة تابع):

user.hi();

هذا لا يعمل:

(user.name == "John" ? user.hi : user.bye)(); // خطأ!

لماذ ؟ إذا كنا نريد ان نفهم لماذا يحدث هذا, دعونا نري خلف الكواليس كيف يعمل ()obj.method.

تفسير النوع المرجعي

عند التدقيق, ربما نلاحظ وجود عمليتين علي عبارة ()obj.method:

  1. اولاً, النقطة '.' تجب الخاصية obj.method.
  2. ثانياً الأقواس () تقوم بتشغيلها.

لذا, كيف يمكن للمعلومات الخاصة بـ this ان تمر من الجزء الأول الى الجزء الثاني?

إذا وضعنا هذه العمليات على خطوط منفصلة, اذا this سوف نقوم بفقدها بالتأكيد:

let user = {
  name: "John",
  hi() { alert(this.name); }
}

// تقسيم الحصول على واستدعاء التابع في سطرين
let hi = user.hi;
hi(); // خطأ, لأن this غير معرفة

هنا hi = user.hi يضع التابع في المتغير, ثم في السطر الأخير يكون مستقلاً تماماً, و في هذه الحالة لا يوجد this.

لجعل ()user.hi تعمل, جافاسكريبت تستخدم خدعة – النقطة '.' لا تيعد تابع, و لكن قيمه من المميز Reference Type.

النوع المرجعي هو “نوع المواصفات”. لا يمكننا استخدامها صراحة, و لكن يتم استخدامها داخلياً بواسطة اللغه.

قيمة النوع المرجعي هي مزيج من ثلاث قيم (base, name, strict), حيث:

  • base الكائن.
  • name إسم الخاصية.
  • strict تكون صحيحة اذا use strict تعمل.

النتيجة من إستخدام user.hi لا يكون تابع, و لكن قيمة من النوع المرجعي. user.hi في الوضع الصارم تكون:

// قيمة النوع المرجعي
(user, "hi", true)

حيث الأقواس () تسمى النوع المرجعي, يتلقون المعلومات الكاملة حول الكائن و توابعه, و يمكن وضع القيمه الصحيحة لـ this (=user في هذه الحالة).

النوع المرجعي هو نوع داخلي خاص “وسيط”, بغرض تمرير المعلومات من النقطة . الي طلب الأقواس ().

اى عملية اخري مثل hi = user.hi تتجاهل النوع المرجعي بالكامل, تأخذ القيمة من user.hi (التابع) و تقوم بتمريره. اذا اى من العمليات المستقبلية “تفقد” this.

لذا, قيمة this يتم تمريرها بالطريقة الصحيحة فقط إذا تم استدعاء التابع مباشرةً باستخدام نقطة obj.method() أو الاقواس المربعة obj['method']() (يقومون بنفس الوظيفه هنا). لاحقًا في هذا البرنامج التعليمي ، سنتعلم طرقًا مختلفة لحل هذه المشكلة مثل func.bind().

الملخص

النوع المرجعي هو نوع داخلي من اللغة.

قراءة خاصية ، كما هو الحال مع النقطة . في obj.method() لا يعيد قيمة الخاصية بالضبط, و لكن “النوع المرجعي” كلاً من قيمة الخاصية والكائن الذي تم أخذها منه.

هذا لاستدعاء الطريقة اللاحقة () للوصل الى الكائن و وضع قيمة this بها.

بالنسبة لجميع العمليات الأخرى ، يصبح النوع المرجعي تلقائيًا قيمة الخاصية (تابع في حالتنا).

جميع آليات العمل مختفيه. لا يهم إلا في الحالات الدقيقة, مثل عندما يتم الحصول على طريقة ديناميكيًا من الكائن ، باستخدام تعبير.

نتيجة النقطة . ليست في الواقع طريقة ، ولكنها قيمة `` يحتاج إلى طريقة لتمرير المعلومات حول obj

مهمه

الأهمية: 2

ما هي نتيجة هذه الشيفرة?

let user = {
  name: "John",
  go: function() { alert(this.name) }
}

(user.go)()

ملاحظة. هناك مأزق :)

خطأ!

حاول هذا:

let user = {
  name: "John",
  go: function() { alert(this.name) }
}

(user.go)() // خطأ!

لا تعطينا رسالة الخطأ في معظم المتصفحات الكثير من الأدلة حول الخطأ الذي حدث.

يظهر الخطأ لأن فاصلة منقوطة مفقودة بعد user = {...}.

لا تقوم جافاسكريبت بإدراج فاصلة منقوطة تلقائيًا قبل قوس ()(user.go), إنها تقوم بقراءة الشيفرة هكذا:

let user = { go:... }(user.go)()

ثم يمكننا أن نرى أيضًا أن هذا التعبير المشترك هو عبارة عن استدعاء للكائن { go: ... } كتابع مع متغير (user.go). وهذا يحدث أيضًا على نفس السطر مع let user, لذلك user لم يتم حتى الآن تعريف الكائن ، ومن هنا كان الخطأ.

إذا أدخلنا الفاصلة المنقوطة ، فكل شيء على ما يرام:

let user = {
  name: "John",
  go: function() { alert(this.name) }
};

(user.go)() // John

يرجى ملاحظة أن الأقواس حول (user.go) لا تفعل شيئ هنا.عادة ما يقومون بإعداد ترتيب العمليات ، ولكن هنا النقطة . تعمل أولاً على أي حال, لذلك ليس هناك تأثير. فقط الشيء الفاصلة المنقوطة هو المهم.

الأهمية: 3

في الشيفرة بالأسفل نريد تنفيذ obj.go() 4 مرات.

لكن تنفيذ (1) و (2) يكون مختلف عن تنفيذ (3) 4 (4). لماذا?

let obj, method;

obj = {
  go: function() { alert(this); }
};

obj.go();               // (1) [object Object]

(obj.go)();             // (2) [object Object]

(method = obj.go)();    // (3) undefined

(obj.go || obj.stop)(); // (4) undefined

هنا يكون التفسير.

  1. هذا هو استدعاء طريقة الكائن المعتاد.

  2. نفس الشيء ، الأقواس لا تغير ترتيب العمليات هنا ، النقطة أولاً على أي حال.

  3. هنا لدينا تنفيذ اكثر تعقيداً (expression).method(). التنفيذ يعمل كما لو كان مقسوم الى سطرين:

    f = obj.go; // حساب المصطلح
    f();        // تنفيذ ما لدينا

    هنا f() يتم تنفيذها كـ تابع, بدون this.

  4. نفس الشيئ في (3), ايسر النقطة . لدينا مصطلح.

لتفسير سلوك (3) و (4) نريد إعادة تنفيذ مدخلات الخاصية (نقطة او اقواس مربعة) تعيد قيمة النوع المرجعي.

اى عملية عليها عدا تنفيذ التابع (مثل = or ||) يحولها إلى قيمة عادية ، لا تحمل المعلومات التي تسمح بتعيينها this.

خريطة الدورة التعليمية

التعليقات

إقرأ هذا قبل أن تضع تعليقًا…
  • إذا كان لديك اقتراحات أو تريد تحسينًا - من فضلك من فضلك إفتح موضوعًا فى جيتهاب أو شارك بنفسك بدلًا من التعليقات.
  • إذا لم تستطع أن تفهم شيئّا فى المقال - وضّح ماهو.
  • إذا كنت تريد عرض كود استخدم عنصر <code> ، وللكثير من السطور استخدم <pre>، ولأكثر من 10 سطور استخدم (plnkr, JSBin, codepen…)