We are still cooking the magic in the way!
الدوال السهمية والمعاملات الافتراضية
مقدمة عن الدوال السهمية
تم تقديم الدوال السهمية في ES6 (ECMAScript 2015) كصيغة اكثر اختصارا لكتابة تعبيرات الدوال. وهي من اكثر ميزات جافاسكريبت الحديثة استخداما، وفهمها بعمق ضروري لاي مطور جافاسكريبت. الدوال السهمية ليست مجرد صيغة اقصر -- فهي تتصرف بشكل مختلف عن الدوال التقليدية في عدة نواحي مهمة تؤثر على كيفية عمل الكود الخاص بك.
قبل الدوال السهمية، في كل مرة كنت تحتاج فيها الى دالة استدعاء راجع صغيرة او دالة مضمنة، كان عليك كتابة الكلمة المفتاحية function بالكامل والاقواس والاقواس المعقوصة وعبارة return. الدوال السهمية تقلل هذا الكود المتكرر بشكل كبير مما يجعل الكود اكثر نظافة وقراءة. ومع ذلك فهي تاتي ايضا بقواعد محددة حول كيفية عمل this وarguments وميزات اخرى مما يعني انه لا يمكنك دائما استخدامها كبديل مباشر للدوال العادية.
صيغة الدالة السهمية الاساسية
تستخدم الدالة السهمية عامل التشغيل => (السهم السمين) لتعريف دالة. تبدا الصيغة العامة بقائمة المعاملات بين اقواس، يليها السهم، ثم جسم الدالة بين اقواس معقوصة. دعنا نقارن تعبير دالة تقليدي مع مكافئه بالدالة السهمية لرؤية الفرق بوضوح.
مثال: الدالة التقليدية مقابل الدالة السهمية
// تعبير دالة تقليدي
const greet = function(name) {
return 'مرحبا، ' + name + '!';
};
// المكافئ بالدالة السهمية
const greetArrow = (name) => {
return 'مرحبا، ' + name + '!';
};
console.log(greet('علي')); // !مرحبا، علي
console.log(greetArrow('علي')); // !مرحبا، علي
كما ترى، الدالة السهمية تزيل الكلمة المفتاحية function وتضع السهم => بين قائمة المعاملات وجسم الدالة. السلوك مطابق في هذه الحالة البسيطة لكن الصيغة اقصر بشكل ملحوظ. يصبح هذا اكثر قوة عند دمجه مع ميزات الارجاع الضمني واختصار المعامل الواحد التي سنستكشفها لاحقا.
الارجاع الضمني
عندما يكون لدى الدالة السهمية تعبير واحد في جسمها، يمكنك حذف الاقواس المعقوصة والكلمة المفتاحية return. يتم ارجاع التعبير تلقائيا. يسمى هذا الارجاع الضمني وهو يجعل الدوال المكونة من سطر واحد مختصرة للغاية. هذا النمط شائع بشكل خاص عند العمل مع توابع المصفوفات مثل map وfilter وreduce.
مثال: الارجاع الضمني
// مع الاقواس المعقوصة وارجاع صريح
const double = (num) => {
return num * 2;
};
// مع الارجاع الضمني (بدون اقواس معقوصة وبدون كلمة return)
const doubleShort = (num) => num * 2;
console.log(double(5)); // 10
console.log(doubleShort(5)); // 10
// امثلة اخرى على الارجاع الضمني
const square = (x) => x * x;
const isEven = (n) => n % 2 === 0;
const toUpper = (str) => str.toUpperCase();
console.log(square(4)); // 16
console.log(isEven(7)); // false
console.log(toUpper('hello')); // HELLO
مثال: ارجاع الكائنات الحرفية
// خطا -- جافاسكريبت تعتقد ان {} هو جسم دالة
const makeUser = (name) => { name: name };
console.log(makeUser('علي')); // undefined
// صحيح -- لف الكائن الحرفي بين اقواس
const makeUserCorrect = (name) => ({ name: name });
console.log(makeUserCorrect('علي')); // { name: 'علي' }
// استخدام اختصار اسماء الخصائص
const createPerson = (name, age) => ({ name, age });
console.log(createPerson('احمد', 30)); // { name: 'احمد', age: 30 }
اختصار المعامل الواحد
عندما يكون للدالة السهمية معامل واحد فقط، يمكنك حذف الاقواس حول قائمة المعاملات. هذا يجعل الصيغة اقصر. ومع ذلك، اذا كان للدالة صفر معاملات او اكثر من معامل واحد، فان الاقواس مطلوبة. توصي العديد من ادلة الاسلوب باستخدام الاقواس دائما للاتساق، لكن فهم هذا الاختصار مهم لانك ستصادفه كثيرا في كود المطورين الاخرين.
مثال: قواعد اقواس المعاملات
// معامل واحد -- الاقواس اختيارية
const increment = x => x + 1;
const greetName = name => 'مرحبا، ' + name;
// صفر معاملات -- الاقواس مطلوبة
const getRandom = () => Math.random();
const sayHello = () => '!مرحبا بالعالم';
// معاملات متعددة -- الاقواس مطلوبة
const add = (a, b) => a + b;
const fullName = (first, last) => first + ' ' + last;
console.log(increment(4)); // 5
console.log(greetName('سارة')); // مرحبا، سارة
console.log(getRandom()); // 0.7234... (عشوائي)
console.log(add(3, 7)); // 10
console.log(fullName('محمد', 'احمد')); // محمد احمد
لا يوجد كائن arguments في الدوال السهمية
احد الفروقات الرئيسية بين الدوال السهمية والدوال العادية هو ان الدوال السهمية لا تملك كائن arguments خاص بها. في الدالة العادية، arguments هو كائن شبيه بالمصفوفة يحتوي على جميع الوسائط الممررة الى الدالة بغض النظر عن عدد المعاملات المعرفة. الدوال السهمية لا تنشئ هذا الكائن. اذا حاولت الوصول الى arguments داخل دالة سهمية، فانه اما سيرمي خطا مرجعي (في النطاق العام) او يرث arguments من دالة عادية محيطة.
مثال: مقارنة كائن arguments
// الدالة العادية -- لها كائن arguments خاص بها
function regularSum() {
let total = 0;
for (let i = 0; i < arguments.length; i++) {
total += arguments[i];
}
return total;
}
console.log(regularSum(1, 2, 3, 4)); // 10
// الدالة السهمية -- لا يوجد كائن arguments
const arrowSum = () => {
// سيرمي خطا مرجعي في الوضع الصارم
// او يشير الى arguments الدالة الخارجية
console.log(typeof arguments); // undefined او موروث
};
// دالة سهمية داخل دالة عادية ترث arguments
function outer() {
const inner = () => {
console.log(arguments); // موروث من outer()
};
inner();
}
outer('ا', 'ب', 'ج'); // Arguments ['ا', 'ب', 'ج']
...args) بدلا من كائن arguments. معاملات الباقي تعطيك مصفوفة حقيقية وهذا افضل من كائن arguments الشبيه بالمصفوفة لانك تستطيع استخدام توابع المصفوفات مثل map وfilter وreduce مباشرة عليها.مثال: استخدام معاملات الباقي بدلا من arguments
// استخدام معاملات الباقي مع الدوال السهمية
const sum = (...numbers) => {
return numbers.reduce((total, num) => total + num, 0);
};
console.log(sum(1, 2, 3)); // 6
console.log(sum(10, 20, 30, 40)); // 100
// معاملات الباقي تعطيك مصفوفة حقيقية
const logArgs = (...args) => {
console.log(Array.isArray(args)); // true
console.log(args.length);
args.forEach(arg => console.log(arg));
};
logArgs('س', 'ص', 'ع');
// true
// 3
// س
// ص
// ع
ربط this المعجمي
الفرق الاكثر اهمية وتاثيرا بين الدوال السهمية والدوال العادية هو كيفية تعاملها مع الكلمة المفتاحية this. الدوال العادية تحدد قيمة this الخاصة بها بناء على كيفية استدعائها -- قد يكون الكائن العام او الكائن الذي استدعى التابع او نسخة جديدة اذا استخدم مع new. الدوال السهمية لا تحدد this خاص بها. بدلا من ذلك ترث this من النطاق المعجمي المحيط -- النطاق الذي تم فيه تعريف الدالة السهمية. يسمى هذا ربط this المعجمي.
هذا السلوك يحل واحدة من اكثر المشاكل شيوعا في جافاسكريبت: فقدان سياق this عند تمرير دوال الاستدعاء الراجع. قبل الدوال السهمية كان المطورون يستخدمون حلولا بديلة مثل var self = this او .bind(this) او تخزين المرجع في متغير. الدوال السهمية تقضي على هذه المشكلة تماما.
مثال: مشكلة this مع الدوال العادية
// المشكلة: يضيع 'this' في دالة استدعاء راجع عادية
const timer = {
seconds: 0,
start: function() {
// 'this' هنا يشير الى كائن المؤقت
console.log(this.seconds); // 0
setInterval(function() {
// 'this' هنا يشير الى الكائن العام (او undefined في الوضع الصارم)
this.seconds++; // لا يحدث timer.seconds
console.log(this.seconds); // NaN
}, 1000);
}
};
// الحل القديم: حفظ 'this' في متغير
const timerFixed = {
seconds: 0,
start: function() {
var self = this; // حفظ المرجع
setInterval(function() {
self.seconds++; // يعمل لان self متغير اغلاق
console.log(self.seconds);
}, 1000);
}
};
مثال: الدوال السهمية تحل مشكلة this
// الدالة السهمية ترث 'this' من start()
const timerArrow = {
seconds: 0,
start: function() {
setInterval(() => {
// 'this' موروث من start()، وهو كائن المؤقت
this.seconds++;
console.log(this.seconds); // 1, 2, 3, ...
}, 1000);
}
};
// مثال اخر مع انماط شبيهة بالاحداث
const counter = {
count: 0,
increment: function() {
// الدالة السهمية تحافظ على 'this' من increment()
const doIncrement = () => {
this.count++;
return this.count;
};
return doIncrement();
}
};
console.log(counter.increment()); // 1
console.log(counter.increment()); // 2
console.log(counter.increment()); // 3
الدوال السهمية مقابل الدوال العادية: متى تستخدم كلا منهما
فهم متى تستخدم الدوال السهمية مقابل الدوال العادية امر حاسم لكتابة جافاسكريبت صحيحة. الدوال السهمية ليست بديلا عالميا للدوال العادية. لكل منهما حالات استخدام محددة حيث يكون الخيار الافضل. اليك دليل شامل لمساعدتك في الاختيار.
استخدم الدوال السهمية عندما:
- كتابة دوال استدعاء راجع قصيرة (مثلا داخل
mapوfilterوreduceوforEach). - تحتاج للحفاظ على قيمة
thisمن النطاق المحيط (مثلا داخلsetTimeoutوsetIntervalاو معالجات الاحداث داخل توابع الصفوف). - كتابة دوال خدمية بسيطة من سطر واحد.
- استخدام انماط البرمجة الوظيفية حيث تفضل الدوال القصيرة والنقية.
استخدم الدوال العادية عندما:
- تعريف توابع كائنات تحتاج ربط
thisخاص بها (توابع على كائنات عادية). - استخدام دوال البناء او توابع النموذج الاولي.
- تحتاج الوصول الى كائن
arguments. - تعريف دوال المولد (الدوال السهمية لا يمكن ان تكون مولدات).
- تحتاج ان تكون الدالة مرفوعة (الدوال السهمية المعينة لمتغيرات لا ترفع).
مثال: متى لا تستخدم الدوال السهمية
// لا تستخدم الدوال السهمية كتوابع كائنات
const person = {
name: 'علي',
// خطا -- الدالة السهمية ليس لها 'this' خاص بها
greetArrow: () => {
console.log('مرحبا، ' + this.name); // 'this' ليس كائن person
},
// صحيح -- الدالة العادية تحصل على 'this' من المستدعي
greetRegular: function() {
console.log('مرحبا، ' + this.name); // 'this' هو كائن person
}
};
person.greetArrow(); // مرحبا، undefined
person.greetRegular(); // مرحبا، علي
// لا تستخدم الدوال السهمية كبناة
const Animal = (name) => {
this.name = name;
};
// const cat = new Animal('قطة'); // TypeError: Animal is not a constructor
// صحيح -- استخدم دالة عادية او صف
function AnimalCorrect(name) {
this.name = name;
}
const cat = new AnimalCorrect('قطة');
console.log(cat.name); // قطة
new لانها لا تملك التابع الداخلي [[Construct]] او خاصية prototype. محاولة استخدام new مع دالة سهمية سيرمي خطا من نوع TypeError. هذا بالتصميم لان الدوال السهمية مخصصة لحالات الاستخدام غير التوابع وغير البناة.الدوال السهمية في الاستدعاءات الراجعة
احد اكثر الاماكن شيوعا لاستخدام الدوال السهمية هو كاستدعاءات راجعة. الاستدعاءات الراجعة هي دوال تمرر كوسائط لدوال اخرى وتستخدم على نطاق واسع في جافاسكريبت -- في معالجات الاحداث والعمليات غير المتزامنة وتوابع المصفوفات. الدوال السهمية تجعل الاستدعاءات الراجعة اقصر واكثر نظافة واقل عرضة للاخطاء فيما يتعلق بسياق this.
مثال: الدوال السهمية كاستدعاءات راجعة
// استدعاء راجع setTimeout
setTimeout(() => {
console.log('هذا يعمل بعد ثانية واحدة');
}, 1000);
// نمط معالج الاحداث (في سياق صف او كائن)
class Button {
constructor(label) {
this.label = label;
this.clicks = 0;
}
init(element) {
// الدالة السهمية تحافظ على 'this' من init()
element.addEventListener('click', () => {
this.clicks++;
console.log(this.label + ' نقر ' + this.clicks + ' مرات');
});
}
}
// سلاسل الوعود
fetch('/api/data')
.then(response => response.json())
.then(data => {
console.log('تم الاستلام:', data);
})
.catch(error => {
console.error('خطا:', error);
});
الدوال السهمية مع توابع المصفوفات
الدوال السهمية تتالق حقا عند استخدامها مع توابع المصفوفات. الجمع بين الصيغة المختصرة والارجاع الضمني يجعل تحويلات البيانات قابلة للقراءة وانيقة. هذا احد اكثر الانماط عملية واستخداما في تطوير جافاسكريبت الحديث. دعنا نستكشف كيف تعمل الدوال السهمية مع كل من توابع المصفوفات الرئيسية.
مثال: الدوال السهمية مع map وfilter وreduce
const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
// map -- تحويل كل عنصر
const doubled = numbers.map(n => n * 2);
console.log(doubled); // [2, 4, 6, 8, 10, 12, 14, 16, 18, 20]
// filter -- الاحتفاظ بالعناصر التي تطابق شرطا
const evens = numbers.filter(n => n % 2 === 0);
console.log(evens); // [2, 4, 6, 8, 10]
// reduce -- تجميع القيم في نتيجة واحدة
const total = numbers.reduce((sum, n) => sum + n, 0);
console.log(total); // 55
// find -- الحصول على اول عنصر يطابق شرطا
const firstAboveFive = numbers.find(n => n > 5);
console.log(firstAboveFive); // 6
// every وsome -- اختبار الشروط
const allPositive = numbers.every(n => n > 0);
console.log(allPositive); // true
const hasNegative = numbers.some(n => n < 0);
console.log(hasNegative); // false
// سلسلة توابع المصفوفات مع الدوال السهمية
const result = numbers
.filter(n => n % 2 !== 0) // الاحتفاظ بالاعداد الفردية: [1, 3, 5, 7, 9]
.map(n => n * n) // تربيعها: [1, 9, 25, 49, 81]
.reduce((sum, n) => sum + n, 0); // المجموع: 165
console.log(result); // 165
مثال: العمل مع مصفوفات الكائنات
const users = [
{ name: 'علي', age: 28, active: true },
{ name: 'احمد', age: 35, active: false },
{ name: 'سارة', age: 22, active: true },
{ name: 'فاطمة', age: 31, active: true }
];
// استخراج اسماء المستخدمين النشطين
const activeNames = users
.filter(user => user.active)
.map(user => user.name);
console.log(activeNames); // ['علي', 'سارة', 'فاطمة']
// ترتيب المستخدمين حسب العمر
const sortedByAge = [...users].sort((a, b) => a.age - b.age);
console.log(sortedByAge.map(u => u.name)); // ['سارة', 'علي', 'فاطمة', 'احمد']
// حساب متوسط العمر
const avgAge = users.reduce((sum, u) => sum + u.age, 0) / users.length;
console.log(avgAge); // 29
القيم الافتراضية للمعاملات
تسمح لك المعاملات الافتراضية بتحديد قيم احتياطية لمعاملات الدوال عندما لا يتم تقديم وسيط او عند تمرير undefined. قبل ES6 كان المطورون يضطرون للتحقق يدويا من الوسائط المفقودة داخل جسم الدالة باستخدام منطق شرطي او عامل || وهو ما كان عرضة للاخطاء لانه لا يمكنه التمييز بين undefined والقيم الزائفة الاخرى مثل 0 و'' وnull. المعاملات الافتراضية توفر حلا اكثر نظافة وموثوقية.
مثال: اساسيات المعاملات الافتراضية
// الطريقة القديمة -- قيم افتراضية يدوية (عرضة للاخطاء)
function greetOld(name) {
name = name || 'ضيف'; // تفشل اذا كان name يساوي ''
return 'مرحبا، ' + name + '!';
}
console.log(greetOld('')); // !مرحبا، ضيف (خطا -- السلسلة الفارغة صالحة)
// الطريقة الحديثة -- معاملات افتراضية
function greetNew(name = 'ضيف') {
return 'مرحبا، ' + name + '!';
}
console.log(greetNew()); // !مرحبا، ضيف
console.log(greetNew('علي')); // !مرحبا، علي
console.log(greetNew('')); // !مرحبا،
console.log(greetNew(undefined)); // !مرحبا، ضيف (الافتراضي يعمل مع undefined)
console.log(greetNew(null)); // !مرحبا، null (null لا يفعل الافتراضي)
// المعاملات الافتراضية تعمل مع الدوال السهمية ايضا
const multiply = (a, b = 1) => a * b;
console.log(multiply(5)); // 5
console.log(multiply(5, 3)); // 15
// معاملات افتراضية متعددة
const createURL = (path = '/', protocol = 'https', domain = 'example.com') => {
return protocol + '://' + domain + path;
};
console.log(createURL()); // https://example.com/
console.log(createURL('/about')); // https://example.com/about
console.log(createURL('/api', 'http', 'localhost')); // http://localhost/api
المعاملات الافتراضية يتم تقييمها في وقت الاستدعاء وليس في وقت التعريف. هذا يعني انك تستطيع استخدام التعبيرات واستدعاءات الدوال وحتى المعاملات المعرفة سابقا كقيم افتراضية. هذا يفتح امكانيات قوية لانشاء توقيعات دوال مرنة.
مثال: القيم الافتراضية الديناميكية
// استخدام التعبيرات كافتراضيات
const getTimestamp = (date = new Date()) => date.toISOString();
console.log(getTimestamp()); // الطابع الزمني الحالي
// استخدام استدعاءات الدوال كافتراضيات
const generateId = () => Math.random().toString(36).substring(2, 9);
const createItem = (name, id = generateId()) => ({ name, id });
console.log(createItem('عنصر')); // { name: 'عنصر', id: 'k3x9f2m' }
// استخدام معاملات سابقة في افتراضيات لاحقة
const createRange = (start, end = start + 10) => {
const range = [];
for (let i = start; i <= end; i++) {
range.push(i);
}
return range;
};
console.log(createRange(1)); // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
console.log(createRange(5, 8)); // [5, 6, 7, 8]
المعاملات الافتراضية مع التفكيك
الجمع بين المعاملات الافتراضية وتعيين التفكيك هو نمط قوي يستخدم على نطاق واسع في جافاسكريبت الحديثة خاصة لكائنات التكوين وخيارات واجهات البرمجة. عندما تقبل دالة كائن خيارات يمكنك تفكيكه في قائمة المعاملات وتوفير افتراضيات للخصائص الفردية وكذلك للكائن بالكامل.
مثال: معاملات مفككة مع افتراضيات
// التفكيك مع افتراضيات للخصائص الفردية
const createUser = ({ name = 'مجهول', role = 'مشاهد', active = true } = {}) => {
return { name, role, active };
};
console.log(createUser());
// { name: 'مجهول', role: 'مشاهد', active: true }
console.log(createUser({ name: 'علي' }));
// { name: 'علي', role: 'مشاهد', active: true }
console.log(createUser({ name: 'احمد', role: 'مشرف' }));
// { name: 'احمد', role: 'مشرف', active: true }
// نمط التكوين لدالة
const fetchData = (url, {
method = 'GET',
headers = {},
timeout = 5000,
retries = 3
} = {}) => {
console.log('جلب:', url);
console.log('الطريقة:', method);
console.log('المهلة:', timeout);
console.log('المحاولات:', retries);
};
fetchData('/api/users');
// جلب: /api/users, الطريقة: GET, المهلة: 5000, المحاولات: 3
fetchData('/api/users', { method: 'POST', timeout: 10000 });
// جلب: /api/users, الطريقة: POST, المهلة: 10000, المحاولات: 3
= {} كافتراضي لكائن المعامل المفكك بالكامل. بدونه سيرمي استدعاء الدالة بدون وسائط خطا من نوع TypeError لان جافاسكريبت لا يمكنها تفكيك undefined. يضمن = {} انه اذا لم يتم تمرير وسيط يستخدم كائن فارغ كاحتياطي ثم تاخذ الافتراضيات الفردية للخصائص مفعولها.مثال: تفكيك المصفوفات مع افتراضيات
// تفكيك المصفوفات في المعاملات مع افتراضيات
const getCoordinates = ([x = 0, y = 0, z = 0] = []) => {
return { x, y, z };
};
console.log(getCoordinates()); // { x: 0, y: 0, z: 0 }
console.log(getCoordinates([5])); // { x: 5, y: 0, z: 0 }
console.log(getCoordinates([3, 7])); // { x: 3, y: 7, z: 0 }
console.log(getCoordinates([1, 2, 3])); // { x: 1, y: 2, z: 3 }
معاملات الباقي
تستخدم معاملات الباقي صيغة ... (النشر/الباقي) لجمع جميع الوسائط المتبقية في مصفوفة حقيقية. على عكس كائن arguments تنتج معاملات الباقي نسخة Array حقيقية مما يعني انك تستطيع استخدام توابع المصفوفات عليها مباشرة. تعمل معاملات الباقي في كل من الدوال العادية والدوال السهمية مما يجعلها البديل الحديث لكائن arguments.
مثال: معاملات الباقي
// جمع كل الوسائط في مصفوفة
const sum = (...numbers) => numbers.reduce((total, n) => total + n, 0);
console.log(sum(1, 2, 3)); // 6
console.log(sum(10, 20, 30, 40)); // 100
// معامل الباقي بعد معاملات مسماة
const logMessage = (level, ...messages) => {
const prefix = '[' + level.toUpperCase() + ']';
messages.forEach(msg => console.log(prefix + ' ' + msg));
};
logMessage('info', 'بدا الخادم', 'يستمع على المنفذ 3000');
// [INFO] بدا الخادم
// [INFO] يستمع على المنفذ 3000
logMessage('error', 'فشل الاتصال', 'اعادة المحاولة خلال 5 ثوان', 'المحاولة 2 من 3');
// [ERROR] فشل الاتصال
// [ERROR] اعادة المحاولة خلال 5 ثوان
// [ERROR] المحاولة 2 من 3
// دمج الباقي مع التفكيك
const getFirst = ([first, ...rest]) => {
console.log('الاول:', first);
console.log('الباقي:', rest);
};
getFirst([10, 20, 30, 40]);
// الاول: 10
// الباقي: [20, 30, 40]
مثال: استخدام عملي لمعاملات الباقي
// دالة تغلف دالة اخرى مع التسجيل
const withLogging = (fn, ...defaultArgs) => {
return (...callArgs) => {
const allArgs = [...defaultArgs, ...callArgs];
console.log('استدعاء مع الوسائط:', allArgs);
const result = fn(...allArgs);
console.log('النتيجة:', result);
return result;
};
};
const add = (a, b) => a + b;
const loggedAdd = withLogging(add);
loggedAdd(3, 5);
// استدعاء مع الوسائط: [3, 5]
// النتيجة: 8
// بناء اداة رياضيات مرنة
const calculate = (operation, ...numbers) => {
switch (operation) {
case 'sum':
return numbers.reduce((a, b) => a + b, 0);
case 'product':
return numbers.reduce((a, b) => a * b, 1);
case 'average':
return numbers.reduce((a, b) => a + b, 0) / numbers.length;
case 'max':
return Math.max(...numbers);
case 'min':
return Math.min(...numbers);
default:
return NaN;
}
};
console.log(calculate('sum', 1, 2, 3, 4)); // 10
console.log(calculate('product', 2, 3, 4)); // 24
console.log(calculate('average', 10, 20, 30)); // 20
console.log(calculate('max', 5, 1, 8, 3)); // 8
(a, ...rest, b) غير صالح ولن يعمل.مثال: قواعد موضع معامل الباقي
// صحيح -- معامل الباقي هو الاخير
const correct = (first, second, ...rest) => {
console.log(first, second, rest);
};
correct(1, 2, 3, 4, 5); // 1, 2, [3, 4, 5]
// خطا -- معامل الباقي ليس الاخير
// const wrong = (...rest, last) => {}; // SyntaxError
// خطا -- معاملات باقي متعددة
// const alsoWrong = (...a, ...b) => {}; // SyntaxError
// دمج المعاملات الافتراضية ومعاملات الباقي
const buildList = (title = 'بدون عنوان', ...items) => {
return { title, items, count: items.length };
};
console.log(buildList('مشتريات', 'حليب', 'بيض', 'خبز'));
// { title: 'مشتريات', items: ['حليب', 'بيض', 'خبز'], count: 3 }
console.log(buildList());
// { title: 'بدون عنوان', items: [], count: 0 }
الجمع بين كل شيء: امثلة شاملة
الان بعد ان فهمت جميع الميزات الفردية، دعنا ندمجها في سيناريوهات عملية توضح كيف تعمل الدوال السهمية والمعاملات الافتراضية والتفكيك ومعاملات الباقي معا في كود حقيقي.
مثال: بناء خط انابيب معالجة البيانات
// خط انابيب معالجة البيانات باستخدام الدوال السهمية
const products = [
{ name: 'حاسوب', price: 999, category: 'الكترونيات', inStock: true },
{ name: 'قميص', price: 29, category: 'ملابس', inStock: true },
{ name: 'سماعات', price: 149, category: 'الكترونيات', inStock: false },
{ name: 'بنطلون', price: 59, category: 'ملابس', inStock: true },
{ name: 'هاتف', price: 699, category: 'الكترونيات', inStock: true },
{ name: 'جاكيت', price: 89, category: 'ملابس', inStock: false }
];
// فلترة قابلة للتكوين مع افتراضيات
const filterProducts = ({
category = null,
maxPrice = Infinity,
onlyInStock = false
} = {}) => {
return products
.filter(p => !category || p.category === category)
.filter(p => p.price <= maxPrice)
.filter(p => !onlyInStock || p.inStock);
};
console.log(filterProducts({ category: 'الكترونيات', onlyInStock: true }));
// [{ name: 'حاسوب', ... }, { name: 'هاتف', ... }]
console.log(filterProducts({ maxPrice: 100 }));
// [{ name: 'قميص', ... }, { name: 'بنطلون', ... }, { name: 'جاكيت', ... }]
// دالة تنسيق مع معاملات الباقي
const formatList = (separator = ', ', ...items) => items.join(separator);
console.log(formatList(' | ', 'HTML', 'CSS', 'JavaScript')); // HTML | CSS | JavaScript
console.log(formatList(undefined, 'ا', 'ب', 'ج')); // ا, ب, ج
تمرين عملي
اكمل التحديات التالية لتعزيز فهمك للدوال السهمية والمعاملات الافتراضية:
- اكتب دالة سهمية تسمى
capitalizeتاخذ سلسلة نصية وترجعها مع الحرف الاول كبير والباقي صغير. استخدم الارجاع الضمني. - انشئ دالة سهمية تسمى
createGreetingتقبلname(افتراضي:'العالم') وgreeting(افتراضي:'مرحبا') وpunctuation(افتراضي:'!'). يجب ان ترجع سلسلة التحية الكاملة. - اكتب دالة سهمية تسمى
pluckتاخذ مصفوفة من الكائنات واسم خاصية وترجع مصفوفة تحتوي فقط على القيم لتلك الخاصية. مثلاpluck([{name: 'ا'}, {name: 'ب'}], 'name')يجب ان ترجع['ا', 'ب']. - انشئ دالة تسمى
pipeتقبل اي عدد من الدوال باستخدام معاملات الباقي وترجع دالة جديدة تمرر مدخلاتها عبر كل دالة بالتسلسل. مثلاpipe(double, addOne)(5)يجب ان ترجع11. - اكتب دالة سهمية
createCounterترجع كائنا بتوابعincrementوdecrementوgetCount، باستخدام الدوال السهمية والاغلاق للحفاظ على العداد خاصا. وضح ان الدوال السهمية الداخلية تلتقطthisبشكل صحيح او تستخدم متغيرات الاغلاق.