دوال الأسهم والكلمة المفتاحية this في جافا سكريبت

آخر تحديث: 02/10/2026
نبذة عن الكاتب: ج مصدر تريل
  • توفر دوال السهم بنيةً موجزةً وطريقةً لالتقاط البيانات this معجميًا من نطاقها المحيط، بدلاً من إنشاء ربطها الخاص.
  • قيمة this يعتمد ذلك في الدوال العادية على كيفية استدعائها، مما يؤثر على الدوال والأساليب والمنشئات والفئات وردود الاتصال.
  • تُعد دوال السهم مثالية لأساليب الاستدعاء وأساليب المصفوفات، ولكنها خيار سيئ لأساليب الكائنات ومعالجات أحداث DOM والمنشئات.
  • فهم متى this إن استخدام الديناميكية مقابل المعجمية أمر ضروري لتجنب الأخطاء الدقيقة وللاختيار بين وظائف السهم والوظائف التقليدية.

دوال الأسهم وهذا في جافا سكريبت

إذا كنت قد سجلت الدخول من قبل this إذا كنت تستخدم وظائف جافا سكريبت مختلفة وحصلت على نتائج متباينة بشكل كبير، فأنت لست وحدك. يواجه العديد من المطورين حالات حيث تقوم دالة ما بطباعة الكائن المتوقع، بينما تقوم دالة السهم بطباعة windowوفجأةً، يشير سهم متداخل "بشكل سحري" إلى الكائن المحيط به. إن فهم سبب حدوث ذلك هو المفتاح لكتابة كود برمجي متوقع وخالٍ من الأخطاء.

دوال الأسهم و this تُعدّ الكلمة المفتاحية واحدة من أهم (وأكثر) التركيبات التي يُساء فهمها في لغة جافا سكريبت الحديثة. تبدو دوال الأسهم وكأنها مجرد صيغة مختصرة، لكنها في الواقع تغير طريقة عملها. this كيفية التعامل مع الدوال المستدعاة، وكيفية عملها، وحتى متى يجب استخدامها كطرق أو لا. دعونا نستعرض كل شيء خطوة بخطوة، من بناء الجملة إلى سياق التنفيذ، باستخدام لغة إنجليزية بسيطة وأمثلة عملية كثيرة.

صيغة دوال الأسهم بدون أي لبس

دوال السهم هي تعبيرات دالية تُكتب باستخدام => بناء الجملة بدلاً من function الكلمة. من الناحية المفاهيمية، يمكنك اعتبارها طريقة مختصرة لكتابة: "خذ هذه المعلمات، وقم بتقييم هذا التعبير أو كتلة التعليمات البرمجية، وأرجع قيمة". في الأساس، لا تزال هذه دوال، لكنها تتصرف بشكل مختلف في عدة جوانب مهمة.

أبسط دالة سهمية ترتبط مباشرة بتعبير الدالة العادية. على سبيل المثال، هذا التعبير الوظيفي الكلاسيكي:

const multiplyByTwo = function (value) { return value * 2; };

يمكن إعادة كتابتها كدالة سهمية على النحو التالي:

const multiplyByTwo = (value) => { return value * 2; };

تتألق دوال الأسهم عندما يكون الجسم عبارة عن تعبير واحد. إذا كان نص الدالة عبارة عن جملة واحدة فقط تُرجع قيمة، فيمكنك حذف كل من الأقواس المعقوفة والعبارات الصريحة. returnمما يتيح إرجاعًا ضمنيًا:

const multiplyByTwo = value => value * 2;

عندما يكون هناك مُعامل واحد فقط، يمكنك حذف الأقواس المحيطة، ولكن فقط في تلك الحالة المحددة. So x => x * 2 صحيح، ولكن إذا كان لديك صفر أو عدة معلمات، فيجب عليك الاحتفاظ بالأقواس:

  • المعلمات الصفرية: () => 42
  • أحد المعايير: x => x * 2 or (x) => x * 2
  • معياران أو أكثر: (x, y) => x + y

عندما تحتاج إلى أكثر من عبارة واحدة في متن الرسالة، يجب عليك استخدام الأقواس المعقوفة و... return. في هذه الحالة، تتصرف دوال السهم مثل الدوال العادية فيما يتعلق بالقيم المرجعة: لا returnلم يتم إرجاع أي قيمة.

const feedCat = (status) => {
if (status === 'hungry') {
return 'Feed the cat';
} else {
return 'Do not feed the cat';
}
};

كن حذرًا عند إرجاع الكائنات الحرفية من الدوال السهمية، لأن أقواس الكائن يمكن الخلط بينها وبين جسم الدالة. لتجنب هذا الغموض، قم بوضع الكائن الحرفي بين قوسين حتى يعرف جافا سكريبت أنه تعبير سيتم إرجاعه:

const toObject = value => ({ result: value });

شيء آخر: دوال السهم هي دائماً تعبيرات، وليست أبداً تصريحات. هذا يعني أنه يجب إسنادها إلى متغير أو خاصية أو تمريرها كوسيط؛ لا يمكنها أن تقف بمفردها مثل function myFunc() {}وهي لا يتم رفعها بنفس طريقة تعريفات الدوال، لذلك لا يمكنك استدعاؤها قبل تعريفها.

ما هو بالضبط this في جافا سكريبت؟

الكلمة الرئيسية this هو ربط ديناميكي تقوم جافا سكريبت بإنشائه لك عند تنفيذ دالة أو طريقة فئة. يمكنك اعتبارها مُعاملاً غير مرئي تعتمد قيمته على كيفية ومكان استدعاء الدالة. هذا ما يجعلها قوية ومرنة، ولكنه أيضاً مصدر كبير للالتباس.

في دالة غير صارمة، this دائماً ما يتم حلها إلى نوع من الكائنات؛ في الوضع الصارم، يمكن أن تكون أي قيمة حرفياً، بما في ذلك undefined. يحدد جافا سكريبت تلك القيمة بناءً على سياق التنفيذ: دالة عادية، أو استدعاء طريقة، أو استدعاء مُنشئ، أو فئة، أو نطاق عام، أو دالة سهمية.

في المستوى الأعلى من نص برمجي كلاسيكي (وليس وحدة نمطية)، this يشير إلى globalThisوهو عادةً ما يكون المتصفح window موضوع. لذا ستكون المقارنة التالية في المتصفح صحيحة:

console.log(this === window); // true

في الدوال التي ليست أسهمًا، this يتم تحديد ذلك بالكامل من خلال موقع الاتصال. إذا اتصلت obj.method()ثم في الداخل method قيمة this is objإذا أخذت نفس الدالة وسميتها بشكل مستقل كـ fn() في الوضع الصارم، this يصبح undefinedفي الوضع غير الصارم، يقوم جافا سكريبت "بالاستبدال" this مع globalThis.

الأهم من ذلك، أن ما يهم ليس مكان تعريف الدالة، بل كيفية استدعائها. يمكن أن توجد طريقة ما في سلسلة النموذج الأولي أو يُعاد تعيينها إلى كائن مختلف، وستظل ترى this أيًا كان الكائن المستخدم فعليًا وقت الاستدعاء. غالبًا ما يؤدي تمرير دالة إلى تغييرها this إلا إذا قمت بإصلاحها بشكل صريح.

توجد أيضًا أدوات للتحكم this صراحة: call, apply, bindو Reflect.apply. تتيح لك هذه الأدوات "حقن" المطلوب this القيمة: fn.call(obj, arg1, arg2) سوف ينفذ fn مع this تعيين إلى objتنطبق قواعد الاستبدال نفسها في الوضع غير الصارم: إذا نجحت null or undefined as this، يتم استبدالها بـ globalThisيتم وضع العناصر الأولية داخل كائنات التغليف الخاصة بها.

تضيف عمليات الاستدعاء طبقة أخرى من التوجيه غير المباشر، لأن this يتم التحكم فيه من قبل الشخص الذي يتصل بك لطلب معاودة الاتصال. طرق تكرار المصفوفات، Promise عادةً ما تستدعي الدوال البانية، وواجهات برمجة التطبيقات المماثلة، دوال رد الاتصال باستخدام this تعيين إلى undefined (أو الكائن العام في الوضع غير المنظم). بعض واجهات برمجة التطبيقات، مثل Array.prototype.forEach or Set.prototype.forEach، قبول منفصل thisArg المعلمة التي يمكنك استخدامها لتعيين رد الاتصال this.

تقوم واجهات برمجة التطبيقات الأخرى باستدعاء وظائف رد الاتصال بشكل مقصود باستخدام وظائف مخصصة. this القيم. على سبيل المثال، reviver حجة ل JSON.parse و مبادئ السلوك replacer لـ JSON.stringify تسلم this ترتبط معالجات الأحداث في DOM بالعنصر الذي يمتلك الخاصية التي تتم معالجتها حاليًا. عند كتابتها بالطريقة "الكلاسيكية"، ترتبط هذه المعالجات بالعنصر الذي تم إرفاقها به.

الفكرة الأساسية: الدوال السهمية لا تُنشئ نفسها this

السمة المميزة لدوال الأسهم هي أنها لا تنشئ أبدًا نسخة جديدة this ربط. بدلاً من ذلك، فإنهم يغلقون (أو "يستولون") على this من البيئة المعجمية المحيطة بها لحظة إنشائها. وعندما يتم تنفيذ السهم لاحقًا، فإنه ببساطة يعيد استخدام تلك القيمة الملتقطة، بغض النظر عن كيفية استدعائها.

عمليًا، تتصرف دالة السهم كما لو كانت مرتبطة تلقائيًا بشكل دائم بـ this من نطاقها الخارجي. ولهذا السبب تُستخدم أساليب مثل call, applyو bind لا يمكن تغيير this بالنسبة لدالة السهم: thisArg يتم تجاهل الوسيط ببساطة. لا يزال بإمكانك تمرير المعاملات العادية من خلالها، ولكن this القيمة مقفلة.

ضع هذا المقتطف البرمجي في النطاق العام لملف نصي:

const arrow = () => console.log(this);
arrow();

لأن السهم مُعرَّف في الكود العام، فإن this هو عالمي this (عادة window (في نص برمجي للمتصفح)، وهذا لا يتغير أبداً. دعوة arrow كدالة عادية، فإن تعيينها لخاصية، أو تمريرها سيؤدي دائمًا إلى تسجيل نفس الكائن العام عند استدعائها في هذا السياق.

يظهر السلوك المثير للاهتمام حقًا عند تضمين دوال الأسهم داخل الدوال أو الطرق العادية. بما أن السهم يلتقط الدالة الخارجية thisوبذلك تصبح أداة قوية لعمليات الاستدعاء التي تحتاج إلى الرجوع إلى الكائن الحاوي لها دون الحاجة إلى الطريقة المعتادة .bind(this) مراسم.

const counter = {
id: 42,
start() {
setTimeout(() => {
console.log(this.id); // uses counter.id
}, 1000);
},
};

If start كنا نستخدم وظيفة مجهولة تقليدية في الداخل setTimeoutستحتاج إلى ربطها يدويًا this أو احفظها في متغير. باستخدام الأسهم، يرث رد الاتصال بشكل طبيعي this من start، والذي هو counter، وبالتالي this.id مطبوعات 42 على النحو المنشود.

ويشرح هذا الربط المعجمي أيضًا السؤال الكلاسيكي "لماذا يفعل this سؤال "التغيير" عند استخدام الأسهم في الكائنات الحرفية. انظر إلى هذين الجسمين:

const obj1 = {
speak() {
console.log(this);
}
};

const obj2 = {
speak: () => {
console.log(this);
}
};

دعوة obj1.speak() مطبوعات obj1، لان speak هي طريقة منتظمة و this يتم تحديدها بناءً على موقع الاتصال. على النقيض من ذلك، obj2.speak() سجلات خارجية this (غالبا window في المتصفحات)، لأن السهم لا يستخدم الكائن كـ thisلا يقوم الكائن الحرفي نفسه بإنشاء كائن جديد this النطاق؛ يقوم جسم الدالة فقط بذلك، وتتخطى دوال السهم هذه الخطوة.

والآن، لنفترض وجود دالة كائن تقوم بإنشاء واستدعاء سهم داخلي على الفور:

const obj3 = {
speak() {
(() => {
console.log(this);
})();
}
};

obj3.speak();

في هذه الحالة، ترث دالة السهم الداخلي this من speak، والذي هو obj3 عندما دعا obj3.speak(). على الرغم من أن السهم عبارة عن دالة متداخلة يتم استدعاؤها فورًا، إلا أنه لا يزال يشير إلى obj3وليس الكائن الكلي. هذا هو جوهر المعجمي this: فهو يتبع النطاق المحيط، وليس موقع استدعاء السهم نفسه.

this عبر الوظائف والكائنات والمنشئات

لإتقان وظائف الأسهم حقًا و thisمن المفيد أن نرى كيف this يعمل في كل سياق رئيسي: الدوال العادية، والأساليب، والمنشئات، والفئات، والنطاق العام. بمجرد أن تصبح تلك القواعد واضحة، يصبح من الأسهل بكثير فهم سلوك السهم.

في دالة بسيطة (غير سهمية)، this يعتمد الأمر بنسبة 100% على كيفية استدعاء الدالة. إذا اتصلت fn() في الوضع الصارم، this is undefinedفي الوضع غير الدقيق، يؤدي الاستبدال إلى this أصبح globalThis. إذا اتصلت obj.fn()، ثم this is obj. يتحرك fn إلى كائن مختلف أو إلى متغير وقيمة this سنتحرك وفقًا لذلك.

في دالة مُعرَّفة على كائن حرفي، this الكائن الذي يتم الوصول إلى الطريقة عليه، وليس بالضرورة الكائن الذي تم تعريف الطريقة فيه أصلاً. If obj.__proto__ يحتوي على طريقة وتقوم باستدعائها obj.method()ثم في الداخل method, this is obj، وليس النموذج الأولي.

تُعدّ الدوال البانية حالة خاصة أخرى: عندما تستدعي دالة باستخدام new, this مرتبط بمثيل الكائن الذي تم إنشاؤه حديثًا. على سبيل المثال، في function User(name) { this.name = name; }، الدعوة new User('Alex') اطقم كامله this الى الجديد User الكائن. إذا أعاد المُنشئ صراحةً كائنًا غير بدائي، فإن هذا الكائن المُعاد يحل محل this كقيمة نهائية لـ new التعبير.

يعتمد بناء الجملة للفئة على هذه القواعد مع سياقين رئيسيين: مثيل وثابت. داخل دالة البناء أو دالة الكائن، this يشير إلى نسخة الفئة التي تعمل معها. داخل الطرق الثابتة أو كتل التهيئة الثابتة، this يشير إلى الفئة نفسها (أو الفئة المشتقة عند استدعائها من خلال الوراثة). يتم تقييم حقول النسخة باستخدام this مرتبط بالمثيل الجديد؛ انظر الحقول الثابتة this كدالة إنشاء الفئة.

تتصرف دوال إنشاء الفئات المشتقة بشكل مختلف قليلاً: إلى أن تقوم باستدعاء super()لا يوجد شيء قابل للاستخدام this. الاحتجاج super() يهيئ this عن طريق التفويض إلى المُنشئ الأساسي؛ لا يُسمح بالإرجاع قبل القيام بذلك في المُنشئ المشتق إلا إذا قمت بإرجاع كائن مختلف بشكل صريح.

وفي السياق العالمي، this يعتمد ذلك على كيفية قيام بيئة جافا سكريبت بتغليف وتنفيذ التعليمات البرمجية الخاصة بك. في برنامج نصي كلاسيكي للمتصفح، المستوى الأعلى this هو الكائن العام؛ في وحدة ES، المستوى الأعلى this دائما undefinedوحدات Node.js CommonJS مُغلّفة داخليًا وتُنفّذ عادةً باستخدام this تعيين إلى module.exportsيتم تنفيذ سمات معالج الأحداث المضمنة في HTML باستخدام this يتم تعيينها للعنصر الذي ترتبط به.

تفصيل دقيق ولكنه مهم: لا تُدخل القيم الحرفية للكائنات نفسها شيئًا جديدًا this نطاق. الكتابة const obj = { value: this }; سيؤدي النص البرمجي إلى obj.value يساوي الخارج thisوليس الكائن. فقط أجسام الدوال (وأجسام الفئات) هي التي تُنشئ بنية مخصصة this الربط؛ تتخطى الأسهم هذه الخطوة عمدًا وترثها.

لماذا تُعدّ الدوال السهمية رائعةً للاستدعاءات (ومتى لا تكون كذلك)

لأن الدوال السهمية تغلق thisإنها مناسبة تمامًا للعديد من سيناريوهات الاستدعاء حيث تريد أن يستمر الاستدعاء في الإشارة إلى الكائن أو السياق المحيط. يُعد هذا مفيدًا بشكل خاص مع المؤقتات والوعود وطرق المصفوفات مثل map, filterو reduce.

تخيل طريقة تحتاج إلى تحديث خاصية ما بشكل متكرر باستخدام setInterval. باستخدام وظيفة تقليدية، this سيتم استخدام الكائن العام افتراضيًا داخل دالة الاستدعاء (أو يكون undefined (في الوضع الصارم)، لذلك this.count لن يشير إلى مثيلك. مع دالة السهم، يستخدم رد الاتصال بشكل طبيعي this من الطريقة الخارجية.

function Counter() {
this.count = 0;

setInterval(() => {
this.count++;
}, 1000);
}

بفضل السهم، this يشير استدعاء الفاصل الزمني إلى Counter على سبيل المثال، ليس window. إذا كانت دالة الاستدعاء هذه دالة عادية، فستحتاج إما إلى .bind(this) أو متغير وسيط مثل const self = this; للحفاظ على المرجع.

تُسهّل دوال الأسهم أيضًا استخدام أساليب المصفوفات في كتابة التعليمات البرمجية، حيث لا تهتم غالبًا بـ this على الإطلاق. عند تمرير دالة تقليدية كدالة رد نداء، فإن الدالة الضمنية this عادة ما يكون undefinedوقد تنسى ذلك. توضح الأسهم بصريًا أن الدالة هي مجرد ربط مباشر بين المدخلات والمخرجات.

const numbers = [1, 2, 3];
const doubled = numbers.map(n => n * 2);

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

تخيل جهازًا يتتبع حياة قطة:

const cat = {
lives: 9,
jump: () => {
this.lives--; // bug: this is not cat
},
};

cat.jump();

منذ jump هو سهم، this لا يشير إلى cat لكن إلى أي حال this كان هذا هو المكان الذي تم فيه إنشاء الكائن الحرفي (غالباً الكائن العام). المقصود this.lives-- إما أن يُطلق استثناءً (في الوضع الصارم) أو يُجري تغييرًا صامتًا على شيء غير ذي صلة. استخدام صيغة طريقة عادية هنا هو الخيار الصحيح.

تتشابه مستمعات أحداث DOM: النمط القياسي this.classList.toggle('on') يعتمد استدعاء الحدث داخله على this كونه العنصر الذي أشعل فتيل الحدث. باستخدام دالة السهم، this لم يعد يشير إلى العنصر، لذلك يتعطل الكود.

const button = document.getElementById('press');

button.addEventListener('click', () => {
this.classList.toggle('on'); // this is not button
});

في هذه الحالة، يجب أن يكون المعالج دالة عادية بحيث this يرتبط هذا العنصر بعنصر الزر بواسطة المتصفح. لا تعمل دوال الأسهم كبدائل مباشرة إذا كان منطقك يتوقع this ليكون الهدف الديناميكي للحدث.

ومن العيوب الأخرى الدقيقة أن دوال السهم مجهولة التركيب النحوي. عادةً لا يكون لها اسم خاص بها (باستثناء أي متغير تُسند إليه)، مما قد يجعل تتبعات المكدس أقل وضوحًا ويجعل الاستدعاء الذاتي أكثر تعقيدًا. في معظم البرامج الواقعية، يُعد هذا الأمر مقبولًا، لكن من المهم تذكره.

حالات خاصة: دوال الوصول، دوال التعيين، الدوال المقيدة، والزوايا الشاذة

تتبع دوال الوصول والتعيين نفس قاعدة "موقع الاستدعاء": this الكائن الذي يتم الوصول إلى الخاصية عليه، وليس الكائن الذي تم تعريفها فيه أصلاً. إذا تم توريث دالة جلب من نموذج أولي وقمت باستدعائها على كائن مشتق، this يشير الجزء الداخلي من الدالة getter إلى الكائن المشتق.

الطرق المرتبطة التي تم إنشاؤها باستخدام Function.prototype.bind يمنحك سلوكًا مشابهًا إلى حد ما لوظائف السهم، ولكن على مستوى الوظائف العادية. عندما تتصل f.bind(obj)، تقوم بإنشاء دالة جديدة this مثبتة بشكل دائم objبغض النظر عن كيفية استدعائها. قد يكون هذا مفيدًا في الفئات عندما تحتاج إلى الحفاظ على this حتى لو تم فصل طريقة ما.

class Example {
constructor() {
this.handleClick = this.handleClick.bind(this);
}

handleClick() {
console.log(this); // always the instance
}
}

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

وهناك أيضاً بعض الحالات الشاذة القديمة حيث this يتصرف بشكل مختلف، كما هو الحال داخل عنصر مهمل with بيان. داخل with (obj) { ... } كتلة، تستدعي دالة هي خاصية من obj يتصرف بشكل فعال كما لو كنت قد كتبت obj.method()، وبالتالي this لا بد أن objينبغي أن تتجنب التعليمات البرمجية الحديثة withلكن فهم هذا الاستثناء يوضح ذلك this لا يزال الأمر يعتمد بشكل أساسي على كيفية تكوين استدعاء الدالة.

تتمتع معالجات الأحداث المضمنة في HTML أيضًا بقاعدة خاصة: يرى كود المعالج المضمن المحيط بها this كعنصر، لكن الدوال الداخلية المعرفة داخل هذا المعالج تعود إلى الوضع العادي this القواعد. لذا فإن الوظيفة التقليدية الداخلية، غير المرتبطة بأي شيء، ستشهد عادةً this as globalThis (أو undefined (في الوضع الصارم)، وليس العنصر.

وأخيرًا، تذكر أن الدوال السهمية لا تحتوي على prototype خاصية ولا يمكن استخدامها كدوال إنشاء مع new. جاري المحاولة new MyArrow() سيؤدي ذلك إلى ظهور خطأ من نوع TypeError. إذا كنت بحاجة إلى دالة يمكنها العمل كدالة إنشاء، فيجب عليك استخدام دالة عادية أو فئة.

إن مراعاة هذه التفاصيل يجعل الاختيار بين وظائف الأسهم والوظائف التقليدية أسهل بكثير. استخدم الأسهم حيث تريد مفردات this وبناءً على صيغة موجزة، والعودة إلى الدوال العادية كلما احتجت إلى الديناميكية التي تعتمد على موقع الاستدعاء. this دلالات السلوك أو البناء.

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

الوظائف ذات الصلة: