We are still cooking the magic in the way!
JavaScript المتقدم (ES6+)
المعاملات الافتراضية
المعاملات الافتراضية
قدم ES6 المعاملات الافتراضية، مما يسمح لك بتحديد قيم افتراضية لمعاملات الدالة. تلغي هذه الميزة الحاجة إلى التحقق اليدوي من المعاملات وتجعل دوالك أكثر قوة وسهولة في الاستخدام.
المعاملات الافتراضية الأساسية
قبل ES6، كان عليك التحقق يدوياً من المعاملات غير المعرفة. الآن يمكنك تعيين القيم الافتراضية مباشرة في توقيع الدالة:
// ES5 - التحقق اليدوي من المعاملات
function greet(name) {
name = name || 'Guest';
return 'Hello, ' + name;
}
// ES6+ - المعاملات الافتراضية
function greetES6(name = 'Guest') {
return `Hello, ${name}`;
}
console.log(greetES6()); // "Hello, Guest"
console.log(greetES6('Alice')); // "Hello, Alice"
// معاملات افتراضية متعددة
function createUser(name = 'Anonymous', role = 'user', active = true) {
return { name, role, active };
}
console.log(createUser());
// { name: 'Anonymous', role: 'user', active: true }
console.log(createUser('Bob', 'admin'));
// { name: 'Bob', role: 'admin', active: true }
نصيحة: المعاملات الافتراضية تُستخدم فقط عندما يكون المعامل
undefined. تمرير null أو 0 أو false أو '' لن يُفعّل القيمة الافتراضية.
القيم الافتراضية المستندة إلى التعبيرات
يمكن أن تكون القيم الافتراضية أي تعبير، وليس مجرد قيم حرفية. يتم تقييمها وقت الاستدعاء:
// استدعاء دالة كافتراضي
function getDefaultName() {
console.log('Getting default name...');
return 'Guest';
}
function greet(name = getDefaultName()) {
return `Hello, ${name}`;
}
greet('Alice'); // لا يوجد إخراج في الـ console - الافتراضي غير مستخدم
greet(); // يسجل: "Getting default name..." ثم يعيد "Hello, Guest"
// تعبير كافتراضي
function calculatePrice(price, tax = price * 0.1) {
return price + tax;
}
console.log(calculatePrice(100)); // 110 (100 + 10)
console.log(calculatePrice(100, 5)); // 105 (100 + 5)
// التاريخ كافتراضي
function logMessage(message, timestamp = Date.now()) {
console.log(`[${timestamp}] ${message}`);
}
مهم: يتم تقييم تعبيرات القيم الافتراضية في كل مرة يتم فيها استدعاء الدالة ويكون المعامل undefined، وليس مرة واحدة عند تعريف الدالة.
استخدام المعاملات السابقة في القيم الافتراضية
يمكن للمعاملات اللاحقة الإشارة إلى المعاملات السابقة في قيمها الافتراضية:
// استخدام معامل سابق في افتراضي لاحق
function createFullName(firstName = 'John', lastName = firstName + 'son') {
return `${firstName} ${lastName}`;
}
console.log(createFullName()); // "John Johnson"
console.log(createFullName('Bob')); // "Bob Bobson"
console.log(createFullName('Alice', 'Smith')); // "Alice Smith"
// مثال أكثر تعقيداً
function createRange(start = 0, end = start + 10, step = 1) {
const range = [];
for (let i = start; i < end; i += step) {
range.push(i);
}
return range;
}
console.log(createRange()); // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
console.log(createRange(5)); // [5, 6, 7, 8, 9, 10, 11, 12, 13, 14]
console.log(createRange(0, 5)); // [0, 1, 2, 3, 4]
console.log(createRange(0, 10, 2)); // [0, 2, 4, 6, 8]
مهم: يمكنك فقط الإشارة إلى المعاملات التي تظهر سابقاً في قائمة المعاملات. المعاملات اللاحقة في "المنطقة الميتة الزمنية" ولا يمكن الوصول إليها.
التعامل مع undefined مقابل null
فهم كيفية تعامل المعاملات الافتراضية مع القيم الخاطئة المختلفة أمر بالغ الأهمية:
function test(value = 'default') {
return value;
}
// undefined يُفعّل الافتراضي
console.log(test(undefined)); // "default"
console.log(test()); // "default"
// القيم الخاطئة الأخرى لا تُفعّل الافتراضي
console.log(test(null)); // null
console.log(test(0)); // 0
console.log(test(false)); // false
console.log(test('')); // ""
console.log(test(NaN)); // NaN
// التعامل العملي مع null
function greet(name = 'Guest') {
// إذا كنت تريد أن يستخدم null أيضاً الافتراضي
name = name ?? 'Guest'; // استخدام عامل الدمج الفارغ
return `Hello, ${name}`;
}
المعاملات الافتراضية مع التفكيك
تعمل المعاملات الافتراضية بشكل رائع مع التفكيك لواجهات برمجة تطبيقات دوال مرنة:
// معامل كائن مع قيم افتراضية
function createButton({
text = 'Click me',
color = 'blue',
size = 'medium',
disabled = false
} = {}) {
return { text, color, size, disabled };
}
console.log(createButton());
// { text: 'Click me', color: 'blue', size: 'medium', disabled: false }
console.log(createButton({ text: 'Submit', color: 'green' }));
// { text: 'Submit', color: 'green', size: 'medium', disabled: false }
// لاحظ = {} في النهاية - يسمح بالاستدعاء بدون معاملات
console.log(createButton()); // يعمل!
// تفكيك المصفوفة مع القيم الافتراضية
function processCoordinates([x = 0, y = 0, z = 0] = []) {
return { x, y, z };
}
console.log(processCoordinates()); // { x: 0, y: 0, z: 0 }
console.log(processCoordinates([10, 20])); // { x: 10, y: 20, z: 0 }
أفضل ممارسة: عند استخدام تفكيك الكائن في المعاملات، أضف دائماً
= {} كافتراضي للمعامل بأكمله. هذا يسمح باستدعاء الدالة بدون أي معاملات.
الانتقال من أنماط ES5
// ES5 - أنماط متعددة للقيم الافتراضية
function oldWay(name, role, active) {
// النمط 1: عامل OR (لديه مشاكل مع القيم الخاطئة)
name = name || 'Guest';
// النمط 2: عامل ثلاثي
role = (role !== undefined) ? role : 'user';
// النمط 3: فحص typeof
active = (typeof active !== 'undefined') ? active : true;
return { name, role, active };
}
// ES6+ - نظيف وواضح
function newWay(name = 'Guest', role = 'user', active = true) {
return { name, role, active };
}
// كلاهما ينتج نفس النتيجة لكن ES6+ أكثر نظافة بكثير
console.log(newWay('Alice', undefined, false));
// { name: 'Alice', role: 'user', active: false }
حالات الاستخدام العملية
// 1. تكوين طلب API
function fetchData(
url,
method = 'GET',
headers = { 'Content-Type': 'application/json' },
timeout = 5000
) {
return fetch(url, {
method,
headers,
signal: AbortSignal.timeout(timeout)
});
}
// 2. الترقيم
function getPaginatedData(
page = 1,
limit = 10,
sortBy = 'createdAt',
order = 'desc'
) {
const offset = (page - 1) * limit;
return {
query: `SELECT * FROM items ORDER BY ${sortBy} ${order} LIMIT ${limit} OFFSET ${offset}`,
page,
limit
};
}
console.log(getPaginatedData());
// { query: "SELECT * FROM items ORDER BY createdAt desc LIMIT 10 OFFSET 0", page: 1, limit: 10 }
// 3. مسجل مع مستويات الخطورة
function log(
message,
level = 'info',
timestamp = new Date().toISOString()
) {
console.log(`[${timestamp}] [${level.toUpperCase()}] ${message}`);
}
log('Application started');
// [2024-02-08T10:30:00.000Z] [INFO] Application started
// 4. دمج التكوينات
function createConfig(
env = 'development',
port = env === 'production' ? 80 : 3000,
debug = env !== 'production'
) {
return { env, port, debug };
}
console.log(createConfig());
// { env: 'development', port: 3000, debug: true }
console.log(createConfig('production'));
// { env: 'production', port: 80, debug: false }
أنماط افتراضية معقدة
// قيم افتراضية متداخلة مع التفكيك
function createNotification({
title = 'Notification',
message = 'You have a new message',
options: {
duration = 3000,
position = 'top-right',
closable = true
} = {}
} = {}) {
return {
title,
message,
options: { duration, position, closable }
};
}
console.log(createNotification());
// القيم الافتراضية الكاملة مطبقة
console.log(createNotification({
title: 'Alert',
options: { duration: 5000 }
}));
// { title: 'Alert', message: 'You have a new message',
// options: { duration: 5000, position: 'top-right', closable: true } }
// مصنع مع معرفات متزايدة
let lastId = 0;
function createItem(name, id = ++lastId) {
return { id, name };
}
console.log(createItem('Item 1')); // { id: 1, name: 'Item 1' }
console.log(createItem('Item 2')); // { id: 2, name: 'Item 2' }
console.log(createItem('Custom', 100)); // { id: 100, name: 'Custom' }
console.log(createItem('Item 3')); // { id: 3, name: 'Item 3' }
نمط المعاملات المطلوبة
// إنشاء "معامل مطلوب" عن طريق الرمي في الافتراضي
function required(paramName) {
throw new Error(`Parameter "${paramName}" is required`);
}
function createUser(
name = required('name'),
email = required('email'),
role = 'user'
) {
return { name, email, role };
}
// هذا يعمل
console.log(createUser('Alice', 'alice@example.com'));
// { name: 'Alice', email: 'alice@example.com', role: 'user' }
// هذا يرمي خطأ
try {
createUser('Bob'); // Error: Parameter "email" is required
} catch (error) {
console.error(error.message);
}
تمرين تطبيقي:
المهمة: أنشئ دالة formatCurrency مع معاملات افتراضية:
- اقبل
amountوcurrency(افتراضي: 'USD') وlocale(افتراضي: 'en-US') وdecimals(افتراضي: 2) - استخدم واجهة برمجة التطبيقات
Intl.NumberFormatلتنسيق العملة - اجعل
decimalsيعتمد علىcurrency(بعض العملات لا تستخدم الكسور العشرية)
الحل:
function formatCurrency(
amount = 0,
currency = 'USD',
locale = 'en-US',
decimals = ['JPY', 'KRW'].includes(currency) ? 0 : 2
) {
return new Intl.NumberFormat(locale, {
style: 'currency',
currency: currency,
minimumFractionDigits: decimals,
maximumFractionDigits: decimals
}).format(amount);
}
console.log(formatCurrency(1234.56)); // "$1,234.56"
console.log(formatCurrency(1234.56, 'EUR', 'de-DE')); // "1.234,56 €"
console.log(formatCurrency(1234.56, 'JPY', 'ja-JP')); // "¥1,235"
console.log(formatCurrency()); // "$0.00"
اعتبارات الأداء
ملاحظة: يتم تقييم تعبيرات المعاملات الافتراضية في كل مرة يتم فيها استدعاء الدالة. تجنب العمليات المكلفة في القيم الافتراضية. إذا كنت بحاجة إلى تهيئة معقدة، فكر في القيام بذلك داخل جسم الدالة بدلاً من ذلك.
الأخطاء الشائعة
// ❌ لا تستخدم عامل OR للقيم الافتراضية بعد الآن
function bad(count) {
count = count || 10; // مشكلة: 0 سيصبح 10!
}
bad(0); // count يصبح 10 (خطأ!)
// ✅ استخدم المعاملات الافتراضية
function good(count = 10) {
return count;
}
good(0); // count هو 0 (صحيح!)
// ❌ لا تنسَ = {} لتفكيك الكائن
function bad({ name, age }) {
return { name, age };
}
// bad(); // Error: Cannot destructure undefined
// ✅ أضف كائن فارغ افتراضي
function good({ name = 'Guest', age = 0 } = {}) {
return { name, age };
}
good(); // يعمل! يعيد { name: 'Guest', age: 0 }
الملخص
في هذا الدرس، تعلمت:
- المعاملات الافتراضية تبسط تعريفات الدوال وتلغي التحقق اليدوي
- تُستخدم القيم الافتراضية فقط عندما تكون المعاملات
undefined، وليس للقيم الخاطئة الأخرى - يمكن أن تكون القيم الافتراضية تعبيرات يتم تقييمها وقت الاستدعاء
- يمكن للمعاملات اللاحقة الإشارة إلى المعاملات السابقة في قيمها الافتراضية
- ادمج القيم الافتراضية مع التفكيك لواجهات برمجة تطبيقات دوال مرنة
- استخدم نمط المعاملات المطلوبة لفرض المعاملات الإلزامية
التالي: في الدرس التالي، سنستكشف معاملات الباقي والمعاملات (Rest Parameters & Arguments) للتعامل مع أعداد متغيرة من معاملات الدالة!