المرحلة 0 · الأسبوع 2 · الدرس 0008 · ~40 دقيقة

Arrow Functions والـ this — الخروج من نظام القواعد

ليه الدرس ده؟ الـ arrows هي الأداة اللي بتكتبها كل يوم في Vue من غير ما تعرف قانون الـ this بتاعها — وسؤالها شبه مضمون في الانترفيو. وهي آخر قطعة في أسبوع الـ this.

🔁 مراجعة سريعة قبل ما تبدأ (~5 دقايق):

١ — الفكرة في 3 جمل

① الـ arrow function مالهاش this خاص بيها خالص — مش بتاخده من شكل النداء، ولا أي قاعدة من الأربعة ليها سلطة عليها.

② لما كودها يقول this، بيتعامل معاملة أي اسم مش موجود عندها: البحث بيطلع لبره في سلسلة البحث (درس 0003!) لحد ما يلاقي this بتاع أقرب دالة عادية حواليها.

③ يعني القانونين بقوا قدامك كاملين: الدالة العادية this بالنداء (dynamic)، والـ arrow this بالكتابة (lexical) — زي المتغيرات بالظبط. دي مش قاعدة خامسة في الترتيب — ده خروج كامل من النظام.

🧱 وقفة قبل الجدول: يعني إيه constructor؟ الكلمة هتظهر في الصف الجاي، ومحدش شرحها لحد دلوقتي — الحقيقة إنها مش موضوع النهارده أصلاً، هي جزء من أسبوع 4 (Prototypes) جاي قدامنا، بس القاعدة الأولى في جدول الـ this محتاجاها عشان نفهم new. خد ملخص كافي دلوقتي، والتفاصيل الكاملة (prototype chain، الوراثة) هتيجي بالراحة إن شاء الله.

constructor مش نوع خاص من الدوال — هي دالة عادية زي أي دالة، بس متكتوبة بنية إنك تناديها بـ new عشان تبني بيها أوبجكتات لها نفس الشكل. الاتفاق العرفي: اسمها بيبدأ بحرف كبير (PascalCase) — ده بس علامة "نادي عليّا بـ new"، مفيش إنفاذ من اللغة نفسها.

new Person("سارة") بتعمل 4 حاجات بالترتيب: (1) بتصنع أوبجكت فاضي جديد {}. (2) بتربطه بـ Person.prototype (تفاصيلها أسبوع 4). (3) بتنادي Person("سارة") والـ this بتاعها = الأوبجكت الجديد ده (ده بالظبط قاعدة 1 اللي بتعرفها). (4) بترجع الأوبجكت ده تلقائياً من غير ما تكتب return.

function Person(name) {
  this.name = name;
}
const sara = new Person("سارة");
sara.name;               // "سارة"
sara instanceof Person;  // true

واللي بيحصل لو نسيت new؟ مفيش سحر — بترجع لقاعدة 4 (default binding) اللي عارفها من 0005:

function Person(name) {
  this.name = name;
}
Person("نسيت new");
globalThis.name;   // "نسيت new"

💥 السطر ده من غير أي 'use strict' — يعني في sloppy mode (الاسم العرفي للوضع الافتراضي في JS، عكس الـ strict mode اللي عرفته في 0005 — مش قاعدة جديدة، هي نفس default binding بتاع رقم 4 بس باسمها). this بترجع globalThis صامتة، فالسطر this.name = name بيكتب متغير name على مستوى الصفحة كله من غير أي تحذير — باگ خطير وهادئ اسمه تلويث المتغيرات العامة (global pollution). لو الكود ده جوه strict mode، بترمي TypeError: Cannot set properties of undefined فوراً بدل ما تتسرب بصمت — ده أحد أسباب أهمية الـ strict mode في العالم الحقيقي.

✓ كل سطر فوق متحقق منه بتشغيل Node.js فعلي (2026-08-13).

٢ — المقارنة الكاملة

دالة عاديةarrow function
الـ this جاي منين؟شكل النداء لحظة التنفيذمكان الكتابة (سلسلة البحث)
call / apply / bindبتحدد الـ this (قاعدة 2)💥 بتتجاهل تماماً
newبتشتغل constructor (قاعدة 1)💥 TypeError فوراً
كـ method لأوبجكتالصح ✓كارثة — هتجيب this الخارجي
كـ callback جوه methodبتضيع this (إلا بحلول 0007)مثالية — بتورث this تلقائياً ✓

✓ كل صف متحقق منه بتشغيل Node.js فعلي (2026-08-06) — بما فيها إن call بترجع نفس نتيجة النداء العادي بالظبط، وإن new بترمي TypeError.

٣ — الحل الرابع والأخير لفخ الـ callback

في 0007 جمعت 3 حلول (bind، الباراميتر، الغلاف). الـ arrow هي الرابع — والأنضف في الاستخدام اليومي:

const user = {
  name: "عمر",
  hi() {
    setTimeout(() => console.log("أهلاً " + this.name), 100);
  }
};
user.hi();   // "أهلاً عمر"

ليه اشتغلت؟ الـ arrow اتكتبت جوه الدالة hi — فلما كودها سأل عن this، البحث طلع لقى this بتاع hi، اللي كان وقتها = user (بقاعدة implicit من النداء user.hi()). مفيش نسخ ولا قفل ولا باراميتر — وراثة بالكتابة. عشان كده هي الاختيار الافتراضي للـ callbacks الداخلية في كود Vue الحقيقي.

٤ — والوش التاني: الفخ القاتل

نفس القوة دي بتنقلب كارثة لو استخدمت الـ arrow في المكان الغلط — كـ method مباشرة:

const user2 = {
  name: "سارة",
  hi: () => console.log("أهلاً " + this.name)
};
user2.hi();   // "أهلاً undefined"

النداء user2.hi() فيه نقطة واضحة... والـ arrow ماهتماش — هي أصلاً مابتبصش للنداء. بصّت لمكان كتابتها: جوه object literal في مستوى الملف — والـ object literal مش دالة، فمفيش this عنده — فالبحث كمّل لبره ولقى this الخارجي. ✓ متحقق بالتشغيل

📏 القاعدة العملية اللي تمشي بيها في شغلك: الـ methods بدوال عادية (بصيغة hi() {} المختصرة)، والـ callbacks الداخلية بـ arrows. كسر القاعدة دي في الاتجاهين هو مصدر معظم باجات الـ this في الدنيا.

٥ — شوفها بعينك: الدالة اللي بتبص لبره

لاحظ حاجة جديدة في المحاكي: frame الـ arrow (متقطع الحدود 🟡) مافيهوش سطر this أصلي — فيه سهم ↑ بيوريك رحلة البحث لبره. توقع قبل كل خطوة!

const user = {
  name: "عمر",
  hi() {
    setTimeout(() => {
      console.log("أهلاً " + this.name);
    }, 100);
  }
};
user.hi();
دوس «خطوة» عشان نبدأ.
Console:
Call Stack — لاحظ frames الـ arrow المتقطعة 🟡

٦ — الربط بـ Vue: مثال حقيقي

في أول الدرس قلت إن الـ arrow هي الأداة اللي بتكتبها كل يوم في Vue من غير ما تعرف قانونها — الوقت حان نثبت الكلام ده بمثال حقيقي، مش مجرد ادعاء.

🔭 سكافولد سريع قبل المثال (مش تفصيل مطلوب تحفظه دلوقتي — مرحلة Vue الكاملة جاية لاحقاً في خطتك): Vue component (بالصيغة القديمة Options API) عبارة عن object فيه data() بترجع المتغيرات، وmethods بترجع الدوال. Vue بينده كل method بالنقطة على المكوّن (component.loadUser()) — يعني this جواها = implicit binding (قاعدة 3 اللي عارفها من 0005) وبتساوي المكوّن نفسه تلقائياً.

السيناريو الشائع: method بتجيب بيانات من السيرفر (fetch) وعايزة تحفظها في المكوّن جوه .then(). المشكلة: .then() بتنادي الـ callback بتاعها بنداء ساكت من غير أي نقطة — نفس فخ setTimeout بالظبط اللي اتعلمته في 0006 و0007:

export default {
  data() { return { user: null }; },
  methods: {
    loadUser() {
      fetch("/api/user").then(function (data) {
        this.user = data;   // 💥
      });
    }
  }
};

الـ function العادية جوه .then() اتنادت ساكت من مكتبة الـ Promise — فبقاعدة default binding، this ضاع بالظبط زي setTimeout(user.hi, 100) اللي شفته قبل كده. النتيجة: كراش أو this.user بيتحط في مكان غلط تماماً.

الحل — بالظبط قسم ٣ فوق، بس في Vue حقيقي:

export default {
  data() { return { user: null }; },
  methods: {
    loadUser() {
      fetch("/api/user").then((data) => {
        this.user = data;   // ✅
      });
    }
  }
};

مفيش فرق جوهري عن مثال user.hi() اللي حللته بنفسك في قسم ٣ — الـ arrow اتكتبت جوه method اسمها loadUser، فورثت this بتاعها وقت الكتابة، ومهما المكتبة اللي هتنادي عليها (fetch، setTimeout، event listener) نادتها ساكت، القفل اللغوي (lexical) ثابت. عشان كده هي الاختيار الافتراضي لأي callback داخل method في Vue الحقيقي.

📏 ونفس فخ قسم ٤ بيحصل لو غلطت في الاتجاه المعاكس — كتبت الـ method نفسها (مش الـ callback جواها) بصيغة arrow:

methods: {
  loadUser: () => {   // ❌
    fetch("/api/user").then((data) => {
      this.user = data;   // 💥
    });
  }
}

نفس آلية user2.hi = () => ... اللي اتحققنا منها في قسم ٤ — arrow مكتوبة كـ method مباشرة بتدور على this بتاع مكان كتابتها مش المكوّن اللي هتتلصق بيه، فمهما نادتها Vue بالنقطة، هي أصلاً مابتبصش للنداء. هنا القاعدة العملية بتوضح قيمتها الحقيقية: methods بدوال عادية دايماً، والـ callbacks الداخلية جواها بس بـ arrows.

🔭 ده تطبيق على آلية this اللي إنت متمكن منها بالفعل — مش تعليم Vue نفسها؛ الـ syntax الكامل (data، methods، templates، الـ Composition API الأحدث) هييجي بالتفصيل لما توصل لمرحلة Vue في خطتك.

٧ — توقع قبل ما تشوف (3 تمارين)

const user = {
  name: "عمر",
  hi() { setTimeout(() => console.log("أهلاً " + this.name), 50); }
};
user.hi();
const app = {
  name: "منصتي",
  show: () => console.log("أهلاً " + this.name)
};
app.show();
const f = () => this;
console.log(f.call({ x: 1 }) === f());

٨ — اختبر نفسك

س1: الـ arrow function بتاخد الـ this بتاعها منين؟

س2: عملت arrowFn.call(obj) — إيه اللي بيحصل؟

س3: ليه الـ arrow كـ method مباشرة على أوبجكت غلطة؟

س4 (سؤال judgment): امتى الـ arrow أنضف من bind كحل لفخ الـ callback؟

٩ — التحدي الأخير: صمّم الحل بإيدك (من غير AI!)

الكود ده فيه فخ this حقيقي — شغّلته بنفسي وبيطبع NaN:

const timer = {
  seconds: 0,
  start() {
    setTimeout(function () {
      console.log(this.seconds + 1);
    }, 100);
  }
};
timer.start();   // NaN

المطلوب في ملف test.js: صلّحه بطريقتين مختلفتين — نسخة بـ bind ونسخة بـ arrow — وجرّب إن الاتنين بيطبعوا 1، واكتب جنب كل نسخة سطر واحد بالعربي (بره الكود 😉) بيقول ليه اشتغلت. ابعتلي النسختين والناتج.

💡 دقة إضافية (من تجربة صاحب الورشة بنفسه): أول نسخة bind كانت .bind(timer) — بالاسم الصريح — وطلعت شغالة. بس لو الـ method اتشالت لأوبجكت تاني (const timer2 = {seconds: 100, start: timer.start}; timer2.start();)، النسخة دي بتفضل شايفة timer القديمة وبتطبع 1 (مش 101 المفروضة) — لأن timer اسم ثابت بتلاقيه سلسلة البحث، مش قيمة بتتغير حسب مين نادى.

الحل: .bind(this) بدل .bind(timer) — لأن this جوه start() بالفعل بيساوي مين ما نادى (implicit binding صح من الأساس)، فـ.bind(this) بتقفل على القيمة الديناميكية الصح بدل اسم ثابت. ✓ متحقق: bind(timer) طلعت 1، bind(this) طلعت 101، الـ arrow طلعت 101 تلقائي من غير أي تعديل — بتشغيل Node فعلي (2026-08-14).

القاعدة العملية: جوه method، دايماً .bind(this) مش .bind(اسم_الأوبجكت) — والـ arrow أصلاً بتاخد نفس الميزة دي مجاناً من غير ما تكتب bind خالص، عشان كده هي الاختيار الافتراضي في كود Vue الحقيقي (قسم ٦).

١٠ — المصدر الأساسي للتعمق