٢٥ مارس ٢٠٢١

الحديث عن الدوال السهمية Arrow functions مرة أخرى

لنرى الدوال السهمية مرّة أخرى.

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

كثيرًا ما نواجه المواقف (في جافا سكريبت بالتحديد) التي نريد أن نكتب فيها دالة صغيرة وننفّذها في مكان آخر.

مثال:

  • ‎arr.forEaoch(func)‎: تُنفّذ ‎forEach‎ الدالة ‎func‎ لكلّ عنصر في المصفوفة.
  • ‎setTimeut(func)‎: يُنفّذ المجدول الداخلي في البيئة دالة ‎func‎.
  • …وغيرها وغيرها.

هذا هو جوهر اللغة، أن نصنع دالة في مكان ونمرّرها إلى مكان آخر.

وفي هذه الدوال عادةً ما لا نريد أن نترك سياقها الحالي، وهنا تأتي الفائدة المخفية للدوال السهمية.

ليس للدوال السهمية مفهوم الأنا this

كما نذكر من فصل «دوال الكائنات، this» فليس في الدوال السهمية مفهوم ‎this‎، ولو حاولت الوصول إلى قيمة ‎this‎ فستأخذها الدالة من الخارج.

فمثلًا يمكننا استعمالها للمرور على العناصر داخل تابِع للكائن:

let group = {
  title: "Our Group",
  students: ["John", "Pete", "Alice"],

  showList() {
    this.students.forEach(
      student => alert(this.title + ': ' + student)
    );
  }
};

group.showList();

استعملنا هنا في ‎forEach‎ الدالة السهمية، وقيمة ‎this.title‎ فيها هي تمامًا القيمة التي يراها التابِع الخارجي ‎showList‎، أي ‎group.title‎.

لو استعملنا هنا الدوال العادية فسنواجه خطأً:

let group = {
  title: "Our Group",
  students: ["John", "Pete", "Alice"],

  showList() {

    this.students.forEach(function(student) {
      // Error: Cannot read property 'title' of undefined
      alert(this.title + ': ' + student);
    });
  }

};

group.showList();

سبب هذا الخطأ هو أنّ التابِع ‎forEach‎ يشغّل الدوال بتمرير ‎this=undefined‎ مبدئيًا، وبذلك تحاول الشيفرة الوصول إلى ‎undefined.title‎.

ليس لهذا أيّ تأثير على الدوال السهمية إذ ليس لها ‎this‎ أساسًا.

لا يمكن تشغيل الدوال السهمية باستعمال ‎new‎ بطبيعة الحال فدون ‎this‎ تواجه حدًّا آخر: لا يمكنك استعمال الدوال السهمية على أنّها مُنشِئات دوال، أي لا يمكنك استدعاءها باستعمال ‎new‎.

الدوال السهمية والربطات هناك فرق بسيط بين الدالة السهمية ‎=>‎ والدالة العادية التي نستدعيها باستعمال ‎.bind(this)‎:

  • يُنشئ التابِع ‎.bind(this)‎ «نسخة مربوطة» من تلك الدالة.
  • لا يصنع السهم ‎=>‎ أيّ نوع من الربطات. الدالة ليس فيها ‎this‎، فقط. يبحث المحرّك عن قيمة ‎this‎ كما يبحث عن أيّ قيمة متغير آخر: في البيئة المُعجمية الخارجية للدالة السهمية.

ليس للدوال السهمية «مُعاملات»

كما وأنّ الدوال السهمية ليس فيها متغير مُعاملات ‎arguments‎.

وهذا أمر رائع حين نتعامل مع المُزخرِفات إذ نُمرّر الاستدعاء حاملًا قيمة ‎this‎ الحالية مع المُعاملات ‎arguments‎.

فمثلًا هنا تأخذ ‎defer(f, ms)‎ دالةً وتُعيد غِلافًا (Wrapper) عليها تُؤجّل الاستدعاء بالمليثوان ‎ms‎ الممرّرة:

function defer(f, ms) {
  return function() {
    setTimeout(() => f.apply(this, arguments), ms);
  };
}

function sayHi(who) {
  alert('Hello, ' + who);
}

let sayHiDeferred = defer(sayHi, 2000);
sayHiDeferred("John"); // ‫Hello, John بعد مرور ثانيتين

يمكن كتابة نفس الشيفرة دون استعمال دالة سهمية هكذا:

function defer(f, ms) {
  return function(...args) {
    let ctx = this;
    setTimeout(function() {
      return f.apply(ctx, args);
    }, ms);
  };
}

هنا لزم أن نصنع المتغيرين الإضافيين ‎args‎ و ‎ctx‎ لتقدر الدالة في ‎setTimeout‎ على أخذ قيمهما.

ملخص

ليس للدوال السهمية:

  • لا ‎this‎.
  • ولا ‎arguments‎.
  • ولا يمكن استدعائها باستعمال ‎new‎.
  • وليس فيها ‎super‎… لم نشرح ذلك بعد ولكنّا سنفعل في الفصل «وراثة الأصناف».

ليس فيها هذا كله لأنّ الغرض منها كتابة شيفرات قصيرة ليس لها سياق تعتمد عليه بل سياقًا تأخذه، وهنا حين «تتألّق» هذه الدوال. ترجمة -وبتصرف- للفصل Arrow functions revisited من كتاب The JavaScript language

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