٢٥ مارس ٢٠٢١

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

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

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

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

قد تفقد استدعاء تابع تم تقييمه بشكل ديناميكي 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.

So, as the result, the value of this is only passed the right way if the function is called directly using a dot obj.method() or square brackets obj['method']() syntax (they do the same here). There are various ways to solve this problem such as func.bind().

الملخص

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

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

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

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

The whole mechanics is hidden from our eyes. It only matters in subtle cases, such as when a method is obtained dynamically from the object, using an expression.

مهمه

الأهمية: 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. Here we have a more complex call (expression)(). The call works as if it were split into two lines:

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

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

  4. The similar thing as (3), to the left of the parentheses () we have an expression.

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

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

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

التعليقات

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