404 Not Found

404 Not Found


nginx

الدوال في جافاسكريبت

الدوال هي كتلة كود قابلة لإعادة الاستخدام — اكتبها مرة واحدة، واستخدمها بلا نهاية. فكّر فيها كوصفات: حدد الخطوات مرة واحدة، ثم اتبعها كلما طبخت. مبرمج لا يستخدم الدوال مثل شخص يخترع كل طبق من الصعب — مُرهق وعرضة للأخطاء.

ما هي الدالة؟

الدالة "تُغلّف" قطعة منطق، تعطيها اسمًا، وتتيح لك استدعائها متى احتجت:

HTML
<!DOCTYPE html>
<html lang="ar">
<head><meta charset="UTF-8"><title>استدعاء الدوال</title></head>
<body>
<div id="output"></div>
<script>
function sayHello() {
  document.getElementById('output').textContent += 'مرحبا!\n';
}

sayHello();
sayHello();
</script>
</body>
</html>

فوائد الدوال:

إعلان الدالة مقابل تعبير الدالة

هناك طريقتان رئيسيتان لإنشاء الدوال:

HTML
<script>
// إعلان الدالة
function greet(name) {
  return `مرحبا، ${name}!`;
}

// تعبير الدالة
let greet2 = function(name) {
  return `مرحبا، ${name}!`;
};
</script>

الفرق الرئيسي: إعلانات الدوال تُرفع — يمكنك استدعائها قبل ظهورها في الكود. تعبيرات الدوال لا تُرفع; يجب تعريفها أولاً.

HTML
<script>
// إعلان الدالة: استدعاء قبل التعريف — يعمل بشكل جيد
hello();  // يعمل بدون خطأ
function hello() { console.log('مرحبا'); }

// تعبير الدالة: استدعاء قبل التعريف — خطأ
// hi();     // TypeError: hi is not a function
// let hi = function() { console.log('مرحبا'); };
</script>
⚠️ الوصول إلى متغير let قبل إعلانه يُطلق خطأ "المنطقة الزمنية الميتة" (TDZ) — وليس undefined. لذلك عرّف دائمًا تعبيرات الدوال قبل استخدامها.

المعلمات والمعاملات الافتراضية

تقبل الدوال البيانات الخارجية عبر المعلمات:

HTML
<script>
function add(a, b) {
  return a + b;
}
add(1, 2);  // 3
</script>

المعلمات التي لا تمررها تكون undefined. قدم ES6 المعلمات الافتراضية:

HTML
<script>
function greet(name, greeting = 'مرحبا') {
  return `${greeting}، ${name}!`;
}

greet('سارة');           // 'مرحبا، سارة!'
greet('سارة', 'أهلا');    // 'أهلا، سارة!'
</script>
💡 ضع المعلمات الافتراضية في نهاية القائمة. وضعها أولاً بلا معنى — لا يمكن للمتصلين تخطيها لتمرير اللاحقة.

القيم المُرجعة

return تُرسل نتيجة إلى المتصل وتوقف الدالة فورًا:

HTML
<script>
function square(n) {
  return n * n;
}

let result = square(5);  // 25
</script>

دالة بدون return (أو مع return; فقط) تُرجع undefined.

HTML
<script>
function doNothing() {
  // لا يوجد return
}
doNothing();  // undefined
</script>
⚠️ سطر جديد بعد return يسبب إدخال نقطة فاصلة تلقائي، مما يجعلها تُرجع undefined:

return     // يصبح: return; undefined
  obj;     // هذا السطر لا يُنفذ أبدًا

الحل: احتفظ بالقيمة المُرجعة على نفس السطر، أو لفّها بأقواس.

الدوال السهمية (=>)

الدوال السهمية هي بنية أقصر قُدمت في ES6:

HTML
<script>
// دالة عادية
let double = function(n) {
  return n * 2;
};

// دالة سهمية
let double2 = (n) => n * 2;

// جمل متعددة تتطلب أقواسًا وreturn صريح
let greet = (name) => {
  let msg = `مرحبا، ${name}!`;
  return msg;
};
</script>

قواعد البناء:

مثال: استخدام الدوال الأساسي

HTML
<!DOCTYPE html>
<html>
<body>
  <h2>استخدام الدوال الأساسي</h2>
  <div id="output"></div>
  <script>
    function add(a, b) {
      return a + b;
    }

    function greet(name, greeting) {
      if (greeting === undefined) greeting = 'مرحبا';
      return greeting + '، ' + name + '!';
    }

    let multiply = (a, b) => a * b;

    let square = n => n * n;

    let html = '';
    html += 'add(3, 5) = ' + add(3, 5) + '<br>';
    html += 'add(10, 20) = ' + add(10, 20) + '<br>';
    html += "greet('سارة') = " + greet('سارة') + '<br>';
    html += "greet('سارة', 'أهلا') = " + greet('سارة', 'أهلا') + '<br>';
    html += 'multiply(4, 6) = ' + multiply(4, 6) + '<br>';
    html += 'square(7) = ' + square(7) + '<br>';

    document.getElementById('output').innerHTML = html;
  </script>
</body>
</html>
▶ جرّب الكود

مثال: حاسبة بسيطة

HTML
<!DOCTYPE html>
<html>
<body>
  <h2>حاسبة بسيطة</h2>
  <input type="number" id="numA" placeholder="الرقم 1">
  <select id="op">
    <option value="+">+</option>
    <option value="-">-</option>
    <option value="*">&times;</option>
    <option value="/">&divide;</option>
  </select>
  <input type="number" id="numB" placeholder="الرقم 2">
  <button onclick="calc()">احسب</button>
  <p id="result"></p>
  <script>
    function calc() {
      let a = Number(document.getElementById('numA').value);
      let b = Number(document.getElementById('numB').value);
      let op = document.getElementById('op').value;
      let res;

      switch (op) {
        case '+': res = a + b; break;
        case '-': res = a - b; break;
        case '*': res = a * b; break;
        case '/':
          if (b === 0) {
            document.getElementById('result').innerHTML = 'لا يمكن القسمة على صفر!';
            return;
          }
          res = a / b;
          break;
      }

      document.getElementById('result').innerHTML =
        a + ' ' + op + ' ' + b + ' = <strong>' + res + '</strong>';
    }
  </script>
</body>
</html>
▶ جرّب الكود

نطاق الدالة

المتغيرات المعلنة داخل دالة محلية — ت tồnيد فقط داخل تلك الدالة. هذا هو النطاق:

HTML
<script>
function test() {
  let x = 10;    // متغير محلي
  const y = 20;
  var z = 30;
}
test();
// console.log(x);  // ReferenceError: x is not defined
</script>

يمكن للدوال الوصول إلى المتغيرات الخارجية (أساس الإغلاق)، لكن الكود الخارجي لا يمكنه الوصول إلى المتغيرات داخل الدالة:

HTML
<!DOCTYPE html>
<html lang="ar">
<head><meta charset="UTF-8"><title>نطاق الدالة</title></head>
<body>
<pre id="output"></pre>
<script>
let global = 'متغير عام';

function show() {
  let local = 'متغير محلي';
  document.getElementById('output').textContent +=
    'داخل الدالة — global: ' + global + '\n' +
    'داخل الدالة — local: ' + local + '\n';
}
show();
document.getElementById('output').textContent +=
  'خارج الدالة — global: ' + global + '\n' +
  'خارج الدالة — local: غير قابل للوصول (متغير محلي)';
</script>
</body>
</html>
⚠️ تعيين متغير داخل دالة بدون let/const/var يُنشئ متغيرًا عامًا — مصدر شائع للأخطاء. أعلن دائمًا المتغيرات بـ let أو const.

مثال: عرض النطاق

HTML
<!DOCTYPE html>
<html>
<body>
  <h2>نطاق الدالة</h2>
  <div id="output"></div>
  <script>
    let outer = 'متغير خارجي';

    function demo() {
      let inner = 'متغير داخلي';
      let html = '';
      html += 'داخل الدالة — outer: ' + outer + '<br>';
      html += 'داخل الدالة — inner: ' + inner + '<br>';

      function nested() {
        let deep = 'متغير متداخل';
        html += 'الدالة المتداخلة — outer: ' + outer + '<br>';
        html += 'الدالة المتداخلة — inner: ' + inner + '<br>';
        html += 'الدالة المتداخلة — deep: ' + deep + '<br>';
      }
      nested();
      return html;
    }

    let result = demo();
    result += 'خارج الدالة — outer: ' + outer + '<br>';
    result += 'خارج الدالة — inner: غير قابل للوصول (محلي)';

    document.getElementById('output').innerHTML = result;
  </script>
</body>
</html>
▶ جرّب الكود

مقدمة في الاستدعاءات الخلفية

جافاسكريبت تتيح لك تمرير دالة كوسيط لدالة أخرى — الدالة المُمرَّرة تسمى استدعاء خلفي:

HTML
<!DOCTYPE html>
<html lang="ar">
<head><meta charset="UTF-8"><title>الاستدعاءات الخلفية</title></head>
<body>
<pre id="output"></pre>
<script>
function processArray(arr, callback) {
  for (let item of arr) {
    callback(item);
  }
}

let names = ['سارة', 'أحمد', 'محمد'];
processArray(names, function(name) {
  document.getElementById('output').textContent += 'مرحبا، ' + name + '\n';
});
</script>
</body>
</html>

لقد استخدمت بالفعل الاستدعاءات الخلفية — وسائط forEach وmap وfilter كلها دوال استدعاء خلفي:

HTML
<script>
// الدالة المجهولة هنا هي استدعاء خلفي
[1, 2, 3].forEach(function(n) {
  console.log(n);
});

// الدوال السهمية تجعلها أكثر اختصارًا
[1, 2, 3].forEach(n => console.log(n));
</script>
💡 الاستدعاءات الخلفية هي روح جافاسكريبت. معالجات الأحداث، والمؤقتات، وطلبات الشبكة كلها تعتمد على آلية الاستدعاء الخلفي. في الوقت الحالي، فقط تذكر: "يمكن تمرير دالة كوسيط". سنذهب أعمق لاحقًا.

مثال: الاستدعاءات الخلفية في العمل

HTML
<!DOCTYPE html>
<html>
<body>
  <h2>دوال الاستدعاء الخلفي</h2>
  <div id="output"></div>
  <script>
    function processList(items, action) {
      let results = [];
      for (let item of items) {
        results.push(action(item));
      }
      return results;
    }

    let nums = [1, 2, 3, 4, 5];

    let doubled = processList(nums, n => n * 2);
    let squared = processList(nums, n => n * n);
    let labeled = processList(nums, n => 'عنصر ' + n);

    let html = '';
    html += 'الأصلي: [' + nums.join(', ') + ']<br><br>';

    html += '<strong>استدعاء خلفي: n =&gt; n * 2</strong><br>';
    html += 'النتيجة: [' + doubled.join(', ') + ']<br><br>';

    html += '<strong>استدعاء خلفي: n =&gt; n * n</strong><br>';
    html += 'النتيجة: [' + squared.join(', ') + ']<br><br>';

    html += '<strong>استدعاء خلفي: n =&gt; "عنصر " + n</strong><br>';
    html += 'النتيجة: [' + labeled.join(', ') + ']<br><br>';

    html += '<strong>استدعاء خلفي setTimeout (يظهر بعد ثانيتين):</strong><br>';
    html += '<span id="timer">في الانتظار...</span>';

    document.getElementById('output').innerHTML = html;

    setTimeout(function() {
      document.getElementById('timer').textContent = "انتهى الوقت! تم تنفيذ الاستدعاء الخلفي.";
      document.getElementById('timer').style.color = 'green';
    }, 2000);
  </script>
</body>
</html>
▶ جرّب الكود

📖 ملخص

  1. إعلانات الدوال تُرفع; تعبيرات الدوال لا تُرفع — التعريف قبل الاستخدام هو الأسلم
  2. المعلمات الافتراضية تضيف مرونة; ضعها في نهاية قائمة المعلمات
  3. return تنهي الدالة وتعيد قيمة; بدونها، تُرجع الدالة undefined
  4. الدوال السهمية (a, b) => a + b هي اختصار — سطر واحد يُرجع تلقائيًا، multi-line يحتاج `{}``
  5. المتغيرات المعلنة داخل دالة محلية وغير مرئية من الخارج
  6. الاستدعاءات الخلفية = تمرير دالة كوسيط — تُستخدم بواسطة forEach وsetTimeout والمزيد

❓ أسئلة شائعة

س ما الفرق بين الدوال السهمية والدوال العادية؟
ج بالإضافة إلى بنية أقصر، الدوال السهمية ليس لها this الخاص بها — تُرثه من النطاق المحيط. عندما تحتاج this للإشارة إلى الكائن الحالي (في الأساليب أو معالجات الأحداث)، استخدم دالة عادية. للدوال البسيطة المساندة، بنية السهم تعمل بشكل رائع.
س هل يمكن لدالة إرجاع عدة قيم؟
ج ليس مباشرة، لكن يمكنك إرجاع مصفوفة أو كائن وإزالة هيكلته: function getInfo() { return [name, age]; } — ثم استدعِ بـ let [name, age] = getInfo().
س ماذا لو كان لدي معلمات كثيرة جدًا؟
ج عندما يكون لديك أكثر من 3 معلمات، فكّر في استخدام كائن: function createUser({ name, age, city }) {} — استدعِه بـ createUser({ name: 'سارة', age: 20, city: 'القاهرة' }). الترتيب لا يهم، وهو أكثر قراءة بكثير.

📝 تamarين

  1. اكتب دالة isEven(n) تُرجع true إذا كان الرقم زوجيًا، false خلاف ذلك. اختبرها بقيم متعددة على الصفحة.
  2. اكتب دالة formatPrice(price) تأخذ رقمًا وتعيد نص سعر مُنسق (مثال: formatPrice(9.9) يُرجع "$9.90"). استخدم بنية الدالة السهمية.
  3. ابنِ "مولد ترحيب": استخدم دالة استدعاء خلفي تأخذ مصفوفة أسماء ودالة ترحيب، ثم اعرض ترحيبات شخصية لكل شخص على الصفحة.
100%