الدوال في جافاسكريبت
الدوال هي كتلة كود قابلة لإعادة الاستخدام — اكتبها مرة واحدة، واستخدمها بلا نهاية. فكّر فيها كوصفات: حدد الخطوات مرة واحدة، ثم اتبعها كلما طبخت. مبرمج لا يستخدم الدوال مثل شخص يخترع كل طبق من الصعب — مُرهق وعرضة للأخطاء.
ما هي الدالة؟
الدالة "تُغلّف" قطعة منطق، تعطيها اسمًا، وتتيح لك استدعائها متى احتجت:
<!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>
فوائد الدوال:
- قابلية إعادة الاستخدام: اكتب مرة واحدة، استدعِ في كل مكان
- التغليف: خفِّ التعقيد، اعرض فقط الواجهة
- قابلية الصيانة: غيّرها في مكان واحد، كل مستفيد يستفيد
إعلان الدالة مقابل تعبير الدالة
هناك طريقتان رئيسيتان لإنشاء الدوال:
<script>
// إعلان الدالة
function greet(name) {
return `مرحبا، ${name}!`;
}
// تعبير الدالة
let greet2 = function(name) {
return `مرحبا، ${name}!`;
};
</script>
الفرق الرئيسي: إعلانات الدوال تُرفع — يمكنك استدعائها قبل ظهورها في الكود. تعبيرات الدوال لا تُرفع; يجب تعريفها أولاً.
<script>
// إعلان الدالة: استدعاء قبل التعريف — يعمل بشكل جيد
hello(); // يعمل بدون خطأ
function hello() { console.log('مرحبا'); }
// تعبير الدالة: استدعاء قبل التعريف — خطأ
// hi(); // TypeError: hi is not a function
// let hi = function() { console.log('مرحبا'); };
</script>
let قبل إعلانه يُطلق خطأ "المنطقة الزمنية الميتة" (TDZ) — وليس undefined. لذلك عرّف دائمًا تعبيرات الدوال قبل استخدامها.
المعلمات والمعاملات الافتراضية
تقبل الدوال البيانات الخارجية عبر المعلمات:
<script>
function add(a, b) {
return a + b;
}
add(1, 2); // 3
</script>
المعلمات التي لا تمررها تكون undefined. قدم ES6 المعلمات الافتراضية:
<script>
function greet(name, greeting = 'مرحبا') {
return `${greeting}، ${name}!`;
}
greet('سارة'); // 'مرحبا، سارة!'
greet('سارة', 'أهلا'); // 'أهلا، سارة!'
</script>
القيم المُرجعة
return تُرسل نتيجة إلى المتصل وتوقف الدالة فورًا:
<script>
function square(n) {
return n * n;
}
let result = square(5); // 25
</script>
دالة بدون return (أو مع return; فقط) تُرجع undefined.
<script>
function doNothing() {
// لا يوجد return
}
doNothing(); // undefined
</script>
return يسبب إدخال نقطة فاصلة تلقائي، مما يجعلها تُرجع undefined:
return // يصبح: return; undefined
obj; // هذا السطر لا يُنفذ أبدًا
الحل: احتفظ بالقيمة المُرجعة على نفس السطر، أو لفّها بأقواس.
الدوال السهمية (=>)
الدوال السهمية هي بنية أقصر قُدمت في ES6:
<script>
// دالة عادية
let double = function(n) {
return n * 2;
};
// دالة سهمية
let double2 = (n) => n * 2;
// جمل متعددة تتطلب أقواسًا وreturn صريح
let greet = (name) => {
let msg = `مرحبا، ${name}!`;
return msg;
};
</script>
قواعد البناء:
- معلمة واحدة: الأقواس اختيارية —
n => n * 2 - صفر أو عدة معلمات: الأقواس مطلوبة —
() => 'مرحبا'،(a, b) => a + b - تعبير سطر واحد: يُرجع تلقائيًا; multi-line: يحتاج
{}+return
مثال: استخدام الدوال الأساسي
<!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>
مثال: حاسبة بسيطة
<!DOCTYPE html>
<html>
<body>
<h2>حاسبة بسيطة</h2>
<input type="number" id="numA" placeholder="الرقم 1">
<select id="op">
<option value="+">+</option>
<option value="-">-</option>
<option value="*">×</option>
<option value="/">÷</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يد فقط داخل تلك الدالة. هذا هو النطاق:
<script>
function test() {
let x = 10; // متغير محلي
const y = 20;
var z = 30;
}
test();
// console.log(x); // ReferenceError: x is not defined
</script>
يمكن للدوال الوصول إلى المتغيرات الخارجية (أساس الإغلاق)، لكن الكود الخارجي لا يمكنه الوصول إلى المتغيرات داخل الدالة:
<!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.
مثال: عرض النطاق
<!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>
مقدمة في الاستدعاءات الخلفية
جافاسكريبت تتيح لك تمرير دالة كوسيط لدالة أخرى — الدالة المُمرَّرة تسمى استدعاء خلفي:
<!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 كلها دوال استدعاء خلفي:
<script>
// الدالة المجهولة هنا هي استدعاء خلفي
[1, 2, 3].forEach(function(n) {
console.log(n);
});
// الدوال السهمية تجعلها أكثر اختصارًا
[1, 2, 3].forEach(n => console.log(n));
</script>
مثال: الاستدعاءات الخلفية في العمل
<!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 => n * 2</strong><br>';
html += 'النتيجة: [' + doubled.join(', ') + ']<br><br>';
html += '<strong>استدعاء خلفي: n => n * n</strong><br>';
html += 'النتيجة: [' + squared.join(', ') + ']<br><br>';
html += '<strong>استدعاء خلفي: n => "عنصر " + 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>
📖 ملخص
- إعلانات الدوال تُرفع; تعبيرات الدوال لا تُرفع — التعريف قبل الاستخدام هو الأسلم
- المعلمات الافتراضية تضيف مرونة; ضعها في نهاية قائمة المعلمات
returnتنهي الدالة وتعيد قيمة; بدونها، تُرجع الدالةundefined- الدوال السهمية
(a, b) => a + bهي اختصار — سطر واحد يُرجع تلقائيًا، multi-line يحتاج `{}`` - المتغيرات المعلنة داخل دالة محلية وغير مرئية من الخارج
- الاستدعاءات الخلفية = تمرير دالة كوسيط — تُستخدم بواسطة
forEachوsetTimeoutوالمزيد
❓ أسئلة شائعة
this الخاص بها — تُرثه من النطاق المحيط. عندما تحتاج this للإشارة إلى الكائن الحالي (في الأساليب أو معالجات الأحداث)، استخدم دالة عادية. للدوال البسيطة المساندة، بنية السهم تعمل بشكل رائع.function getInfo() { return [name, age]; } — ثم استدعِ بـ let [name, age] = getInfo().function createUser({ name, age, city }) {} — استدعِه بـ createUser({ name: 'سارة', age: 20, city: 'القاهرة' }). الترتيب لا يهم، وهو أكثر قراءة بكثير.📝 تamarين
- اكتب دالة
isEven(n)تُرجعtrueإذا كان الرقم زوجيًا،falseخلاف ذلك. اختبرها بقيم متعددة على الصفحة. - اكتب دالة
formatPrice(price)تأخذ رقمًا وتعيد نص سعر مُنسق (مثال:formatPrice(9.9)يُرجع"$9.90"). استخدم بنية الدالة السهمية. - ابنِ "مولد ترحيب": استخدم دالة استدعاء خلفي تأخذ مصفوفة أسماء ودالة ترحيب، ثم اعرض ترحيبات شخصية لكل شخص على الصفحة.



