۲۵ اکتبر ۲۰۲۲

پارامترهای رست و سینتکس اسپرد

بسیاری از توابع جاوا‌اسکریپت از تعداد دلخواه آرگومان‌‌ها پشتیبانی می‌کنند.

برای مثال:

  • 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) );

به دلیل آرگومان‌های «بیش از اندازه» هیچ خطایی وجود نخواهد داشت. البته در نتیجه فقط دو مورد اول محاسبه خواهند شد، پس نتیجه کد بالا 3 خواهد بود.

بقیه پارامترها را می‌توان با استفاده از سه نقطه ... در قسمت تعریف تابع گنجاند و به دنبال آن نام آرایه ای که آنها را شامل است نوشت. نقاط به صورت تحت الفظی به معنی “جمع آوری پارامترهای باقی مانده (رست) در یک آرایه” است.

برای مثال، برای جمع آوری تمام آرگومان‌ها در آرایه 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

ما می‌توانیم اولین پارامترها را به عنوان متغیر دریافت کنیم و بقیه را جمع آوری کنیم.

در اینجا دو آرگومان اول به متغیر و بقیه به آرایه title تبدیل می‌شوند:

function showName(firstName, lastName, ...titles) {
  alert( firstName + ' ' + lastName ); // پیام مرادی

  // پارامتر رست به آرایه "title" تبدیل می‌شود
  // یعنی titles = ["مشاور", "امپراطور"]
  alert( titles[0] ); // مشاور
  alert( titles[1] ); // امپراطور
  alert( titles.length ); // 2
}

showName("پیام", "مرادی", "مشاور", "امپراطور");
پارامترهای رست باید در انتها باشند

پارامترهای رست همه آرگومان‌های باقی مانده را جمع آوری می‌کنند، بنابراین موارد زیر منطقی نیستند و باعث ایجاد خطا می‌شوند:

function f(arg1, ...rest, arg2) { // arg2 بعد از ...rest ?!
  // ارور
}

...rest همیشه باید در آخر باشد.

متغیر “arguments”

همچنین یک شی آرایه مانند ویژه به نام arguments وجود دارد که شامل همه آرگومان‌ها بر اساس ترتیب فهرست آنها است.

برای مثال:

function showName() {
  alert( arguments.length );
  alert( arguments[0] );
  alert( arguments[1] );

  // قابل تکرار است
  // for(let arg of arguments) alert(arg);
}

// نشان می‌دهد: 2, پیام, مرادی
showName("پیام", "مرادی");

// نشان می‌دهد: 1, ایلیا, undefined (آرگومان دوم وجود ندارد)
showName("ایلیا");

در زمان های قدیم، ویژگی پارامترهای رست در زبان جاوااسکریپت وجود نداشت و استفاده از “arguments” تنها راه بدست آوردن همه آرگومان‌‌های تابع بود. و هنوز کار می‌کند، می‌توانیم آن را در کد‌های قدیمی پیدا کنیم.

اما نکته منفی این است که اگرچه arguments هم آرایه مانند است و هم قابل تکرار، اما یک آرایه نیست. از متد‌‌های آرایه پشتیبانی نمی‌کند، بنابراین نمی‌توانیم به عنوان مثال arguments.map(...) را فراخوانی کنیم.

همچنین همیشه شامل همه آرگومان‌‌ها است. ما نمی‌توانیم تعداد مشخصی از آنها را به دست بیاوریم، مانند کاری که با پارامتر‌های رست انجام دادیم.

بنابراین وقتی به این ویژگی‌ها نیاز داریم، پارامترهای رست ترجیح داده می‌شود.

توابع تک خطی شی "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 = "سلام";

alert( [...str] ); // س, ل, ا, م

سینتکس اسپرد به صورت داخلی از تکرارکنندگان برای جمع آوری المنت‌‌ها استفاده می‌کند، همانطور که for..of این کار را انجام می‌دهد.

بنابراین، برای یک رشته، for..of کاراکترها را برمی‌گرداند و با استفاده از ...str به “س”، “ل”، “ا”، "م تبدیل می‌شود. لیست کاراکترها به تنظیم کننده آرایه[…str]` منتقل می‌شود.

برای این کار خاص همچنین می‌توانیم از Array.from استفاده کنیم، زیرا یک تکرار شونده (مانند یک رشته) را به یک آرایه تبدیل می‌کند:

let str = "Hello";

// Array.from یک قابل تکرار را به آرایه تبدیل می‌کند
alert( Array.from(str) ); // س, ل, ا, م

نتیجه مانند نتیجه هنگام استفاده از [...str] است.

اما بین Array.from(obj) و [...obj] یک تفاوت ظریف وجود دارد:

  • Array.from هم بر روی “آرایه مانند” و هم بر روی “تکرار شونده” کار می‌کند.
  • سینتکس اسپرد فقط بر روی “تکرار شونده‌ها” کار می‌کند.

بنابراین، برای تبدیل چیزی به یک آرایه، Array.from بیشتر جهانی است.

کپی کردن یک آرایه/شی

زمانی که ما در مورد Object.assign() در گذشته صحبت کردیم را به یاد دارید?

انجام همان کار با سینتکس اسپرد امکان پذیر است.

let arr = [1, 2, 3];

let arrCopy = [...arr]; // یک آرایه را به لیستی از پارامترها گسترش می‌دهد
                        // سپس نتیجه را در یک آرایه جدید قرار می‌دهد

// آیا آریه‌ها محتوای یکسانی دارند؟
alert(JSON.stringify(arr) === JSON.stringify(arrCopy)); // true

// آیا آرایه‌ها برابر هستند؟
alert(arr === arrCopy); // false (مرجع یکسان نیست)

// اصلاح آرایه اولیه باعث ایجاد تغییر در کپی نمی‌شود:
arr.push(4);
alert(arr); // 1, 2, 3, 4
alert(arrCopy); // 1, 2, 3

توجه داشته باشید که برای ایجاد یک کپی از یک شی می‌توان همان کار را انجام داد:

let obj = { a: 1, b: 2, c: 3 };

let objCopy = { ...obj }; // شی را به لیستی از پارامترها گسترش می‌دهد
                          // سپس نتیجه را در یک شی جدید برمی‌گرداند

// آیا شی‌ها محتوای یکسانی دارند؟
alert(JSON.stringify(obj) === JSON.stringify(objCopy)); // true

// آیا شی‌ها برابر هستند؟
alert(obj === objCopy); // false (مرجع یکسان نیست)

// اصلاح شی اولیه باعث ایجاد تغییر در شی کپی‌شده نمی‌شود:
obj.d = 4;
alert(JSON.stringify(obj)); // {"a":1,"b":2,"c":3,"d":4}
alert(JSON.stringify(objCopy)); // {"a":1,"b":2,"c":3}

این روش کپی کردن یک شی بسیار کوتاه‌تر از let objCopy = Object.assign({}, obj) و یا برای یک آرایه let arrCopy = Object.assign([], arr) است، بنابراین ما ترجیح می‌دهیم هر زمان که می‌توانیم از آن استفاده کنیم.

خلاصه

هنگامی که در کد "..." را می‌بینیم، این عبارت یا پارامترهای رست است یا سینتکس اسپرد.

یک راه آسان برای تشخیص تمایز بین آنها وجود دارد:

  • وقتی ... در انتهای پارامترهای تابع قرار دارد، “پارامترهای رست” است و بقیه لیست آرگومان‌ها را در یک آرایه جمع آوری می‌کند.
  • وقتی ... در فراخوانی یک تابع یا در موقعیت مانند آن نوشته می‌شود، آن را" سینتکس اسپرد "می‌نامند و یک آرایه را به یک لیست گسترش می‌دهد.

از الگو‌ها استفاده کنید:

  • پارامترهای رست برای ایجاد توابعی که هر تعداد آرگومان را بپذیرند استفاده می‌شوند.
  • سینتکس اسپرد برای انتقال یک آرایه به توابعی که معمولاً به لیستی از آرگومان‌ها نیاز دارند، استفاده می‌شود.

آنها با هم کمک می‌کنند به جابه‌جایی آسان بین یک لیست و یک آرایه از پارامترها.

تمام آرگومان های یک فراخوانی تابع نیز در “سبک قدیمی” arguments: شی آرایه مانند قابل تکرار، موجود است.

نقشه آموزش