تتوقّع العديد من دوال جافاسكربت المضمّنة في اللغة عددًا من الوُسطاء لا ينتهي.
مثال:
-
Math.max(arg1, arg2, ..., argN)
-- يُعيد أكبر وسيط من الوُسطاء. -
Object.assign(dest, src1, ..., srcN)
-- ينسخ الخصائص منsrc1..N
إلىdest
. - …وهكذا.
سنتعلّم في هذا الفصل كيف نفعل ذلك أيضًا. كما وكيف نمرّر المصفوفات إلى هذه الدوال على أنّها مُعاملات.
المعاملات «البقية» ...
يمكن أن ننادي الدالة بأيّ عدد من الوُسطاء كيفما كانت معرّفة الدالة.
هكذا:
function sum(a, b) { return a + b; } alert( sum(1, 2, 3, 4, 5) );
لن ترى أيّ خطأ بسبب تلك الوُسطاء «الزائدة». ولكن طبعًا فالنتيجة لن تأخذ بالحسبان إلا أوّل اثنين.
يمكن تضمين بقية المُعاملات في تعريف الدالة باستعمال الثلاث نقاط ...
ثمّ اسم المصفوفة التي ستحتويهم. تعني تلك النقط حرفيًا «اجمع المُعاملات الباقية في مصفوفة».
فمثلًا لجمع كلّ الوُسطاء في المصفوفة args
:
function sumAll(...args) { // اسم المصفوفة هو args let sum = 0; for (let arg of args) sum += arg; return sum; } alert( sumAll(1) ); // 1 alert( sumAll(1, 2) ); // 3 alert( sumAll(1, 2, 3) ); // 6
يمكن لو أردنا أن نأخذ المُعاملات الأولى في متغيّرات ونجمع البقية فقط.
هنا نأخذ الوسيطين الأوليين في متغيرات والباقي نرميه في المصفوفة titles
:
function showName(firstName, lastName, ...titles) { alert( firstName + ' ' + lastName ); // Julius Caesar // الباقي نضعه في مصفوفة الأسماء titles // مثلًا titles = ["Consul", "Imperator"] alert( titles[0] ); // Consul alert( titles[1] ); // Imperator alert( titles.length ); // 2 } showName("Julius", "Caesar", "Consul", "Imperator");
يجب أن تُترك المُعاملات البقية إلى النهاية تجمع المُعاملات البقية كلّ الوُسطاء التي بقيت. وبهذا فالآتي ليس منطقيًا وسيتسبّب بخطأ:
function f(arg1, ...rest, arg2) { // الوسيط arg2 بعد ...البقية؟! // خطأ }
يجب أن يكون ...rest
الأخير دومًا.
متغير الوسطاء arguments
هناك كائن آخر شبيه بالمصفوفات يُدعى arguments
ويحتوي على كلّ الوُسطاء حسب ترتيب فهارسها.
مثال:
function showName() { alert( arguments.length ); alert( arguments[0] ); alert( arguments[1] ); // المصفوفة مُتعدَّدة // for(let arg of arguments) alert(arg); } // تعرض: 2, Julius, Caesar showName("Julius", "Caesar"); // تعرض: 1, Ilya, undefined (ما من مُعطى ثانٍ) showName("Ilya");
قديمًا لم تكن المُعاملات البقية موجودة في اللغة ولم يكن لدينا سوى استعمال arguments
لنجلب كلّ مُعاملات الدالة. وما زالت تعمل الطريقة إلى يومنا هذا ويمكن أن تراها في الشيفرات القديمة.
ولكن السلبية هنا هي أنّ arguments
ليست مصفوفة (على الرغم من أنّها شبيهة بالمصفوفات ومُتعدّدة). بهذا لا تدعم توابِع المصفوفات فلا ينفع أن نستدعي عليها arguments.map(...)
مثلًا.
كما وأنّها تحتوي على كل الوُسطاء دومًا. لا يمكن أن نأخذ منها ما نريد كما نفعل مع المُعاملات البقية.
لهذا متى ما احتجنا إلى ميزة كهذه، فالأفضل استعمال المُعاملات البقية بدلًا من arguments
.
ليس للدوال السهمية "arguments"
لو حاولت الوصول إلى كائن الوُسطاء arguments
من داخل الدالة السهمية، فستستلم الناتج من الدالة «الطبيعية» الخارجية. إليك مثالًا:
function f() { let showArg = () => alert(arguments[0]); showArg(); } f(1); // 1
كما نذكر فليس للدوال السهمية قيمة this
تخصّها، أمّا الآن صرنا نعلم بأنّ ليس لها كائن arguments
أيضًا.
مُعامل التوزيع
رأينا كيف نأخذ مصفوفة من قائمة من المُعطيات.
ولكن ماذا لو أردنا العكس من ذلك؟
فمثلًا لنقل أردنا استعمال الدالة المبنية في اللغة Math.max والتي تُعيد أكبر عدد من القائمة:
alert( Math.max(3, 5, 1) ); // 5
لنقل أنّ لدينا المصفوفة [3, 5, 1]
. كيف نستدعي Math.max
عليها؟
لا ينفع تمريرها «كما هي» لأنّ Math.max
يتوقّع قائمةً بالوُسطاء العددية لا مصفوفة واحدة:
let arr = [3, 5, 1]; alert( Math.max(arr) ); // NaN
وطبعًا لا يمكن أن نفكّ عناصر القائمة يدويًا في الشيفرة Math.max(arr[0], arr[1], arr[2])
لأنّنا في حالات لا نعرف كم من عنصر هناك أصلًا. وما إن يتنفّذ السكربت يمكن أن يكون فيه أكبر مما كتبناه أو حتّى لا شيء أصلًا، وسنحصد لاحقًا ما جنته هذه الشيفرة.
عاش مُنقذنا مُعامل التوزيع! عاش عاش عاش! من بعيد نراه مشابهًا تمامًا للمُعاملات البقية، كما ويستعمل ...
، إلّا أنّ وظيفته هي العكس تمامًا.
فحين نستعمل ...arr
في استدعاء الدالة، «يتوسّع» الكائن المُتعدَّد ...arr
إلى قائمة من الوُسطاء.
فمثلًا نعود إلى Math.max
:
let arr = [3, 5, 1]; // (يحوّل التوزيع المصفوفة إلى قائمة من الوُسطاء) alert( Math.max(...arr) ); // 5
يمكن أيضًا أن نمرّر أكثر من مُتعدَّد واحد بهذه الطريقة:
let arr1 = [1, -2, 3, 4]; let arr2 = [8, 3, -8, 1]; alert( Math.max(...arr1, ...arr2) ); // 8
أو حتّى ندمج مُعامل التوزيع مع القيم العادية:
let arr1 = [1, -2, 3, 4]; let arr2 = [8, 3, -8, 1]; alert( Math.max(1, ...arr1, 2, ...arr2, 25) ); // 25
كما يمكن أن نستعمل مُعامل التوزيعة لدمج المصفوفات:
let arr = [3, 5, 1]; let arr2 = [8, 9, 15]; let merged = [0, ...arr, 2, ...arr2]; alert(merged); // 0,3,5,1,2,8,9,15 (0 ثمّ arr ثمّ 2 ثمّ arr2)
استعملنا في الأمثلة أعلاه مصفوفة لنشرح مُعامل التوزيع، إلّا أنّ المُتعدَّدات أيًا كانت تنفع أيضًا.
فمثلًا نستعمل هنا مُعامل التوزيع لنحوّل السلسلة النصية إلى مصفوفة محارف:
let str = "Hello"; alert( [...str] ); // H,e,l,l,o
يستعمل مُعامل التوزيع هذا داخليًا المُعدِّدات لجمع العناصر، كما تفعل حلقة for..of
.
لذا لو استلمت for..of
سلسلةً نصيّة فتُعيد لنا المحارف وتصير ...str
بالقيمة "H","e","l","l","o"
. وهكذا تُمرّر قائمة المحارف إلى مُهيّئ المصفوفة [...str]
.
يمكننا أيضًا لهذه المهمة استعمال Array.from
إذ أنّه يحوّل المُتعدَّد (مثل السلاسل النصية) إلى مصفوفة:
let str = "Hello"; // يُحوّل Array.from المُتعدَّد إلى مصفوفة alert( Array.from(str) ); // H,e,l,l,o
ناتجه هو ذات ناتج [...str]
.
ولكن… هناك فرق ضئيل بين Array.from(obj)
و[...obj]
:
-
يعمل
Array.from
على الشبيهات بالمصفوفات والمُتعدَّدات. - ويعمل مُعامل التوزيع على المُتعدَّدات فقط لا غير.
لذا لو أردت تحويل شيء إلى مصفوفة فالتابِع Array.from
أكثر استعمالًا وشيوعًا.
ملخص
متى رأينا "..."
في الشيفرة نعرف أنّه إمّا المُعاملات البقية وأمّا مُعامل التوزيع.
إليك طريقة بسيطة للتفريق بينهما:
-
حين ترى
...
موجودة في نهاية مُعاملات الدالة فهي «المُعاملات البقية» وستجمع بقية قائمة الوُسطاء في مصفوفة. -
وحين ترى
...
في استدعاء دالة أو ما شابهه فهو «مُعامل توزيع» يوسّع المصفوفة إلى قائمة.
طُرق الاستعمال:
- تُستعمل المُعاملات البقية لإنشاء دوال تقبل أيّ عدد كان من الوُسطاء.
- يُستعمل مُعامل التوزيع لتمرير مصفوفة إلى دوال تطلب (عادةً) قائمة طويلة من الوُسطاء.
كلا الميزتين تساعدك في التنقل بين القائمة ومصفوفة المُعاملات بسهولة ويُسر.
يمكنك أيضًا أن ترى كل وُسطاء استدعاء الدالة «بالطريقة القديمة» arguments
وهو كائن مُتعدَّد شبيه بالمصفوفات.
ترجمة -وبتصرف- للفصل Rest parameters and spread syntax من كتاب The JavaScript language
أفضل التعليقات
لا توجد أية تعليقات بعد
انضم إلى النقاش
يمكنك أن تنشر الآن وتسجل لاحقًا. إذا كان لديك حساب، فسجل الدخول الآن لتنشر باسم حسابك.