Багато вбудованих функцій JavaScript підтримують довільну кількість аргументів.
Наприклад:
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
Ми можемо вказати щоб перші параметри отримувались як аргументи, а в масив пішли всі інші.
У цьому прикладі перші два аргументи переходять у змінні, а решта -- в масив titles
:
function showName(firstName, lastName, ...titles) {
alert( firstName + ' ' + lastName ); // Юлій Цезар
// решта параметрів переходять до масиву
// titles = ["Консул", "Полководець"]
alert( titles[0] ); // Консул
alert( titles[1] ); // Полководець
alert( titles.length ); // 2
}
showName("Юлій", "Цезар", "Консул", "Полководець");
Залишкові параметри збирають усі залишені аргументи, тому такий код не спрацює і викличе помилку:
```js
function f(arg1, ...rest, arg2) { // arg2 після ...rest ?!
// error
}
```
Залишок `...rest` завжди повинен бути останнім.
Існує також спеціальний псевдомасив, об'єкт arguments
, який містить усі аргументи з їх індексами.
Наприклад:
function showName() {
alert(arguments.length);
alert(arguments[0]);
alert(arguments[1]);
// він ітеровуваний (перебираваний)
// for(let arg of arguments) alert(arg); працюватиме
}
// показує: 2, Юлій, Цезар
showName("Юлій", "Цезар");
// показує: 1, Ілля, undefined (другого аргументу нема)
showName("Ілля");
В стародавні часи такої можливості як залишкові параметри в JavaScript не існувало. Тому єдиним способом отримати всі аргументи функції було за допомогою arguments
. І він все ще працює, ми можемо знайти його в старому коді.
Але недоліком є те, що хоч arguments
є одночасно і псевдомасивом, і ітеровуваним (перебираваним) об'єктом, та все ж це об'єкт, а не масив. Він не підтримує методи масиву, тому ми не можемо наприклад викликати arguments.map(...)
.
Крім того, він завжди містить усі аргументи. Ми не можемо отримати лише частину з них, як це можна робити з допомогою залишкових параметрів.
Тому, коли нам потрібні вбудовані методи масивів, тоді краще використати залишкові параметри.
````smart header="Стрíлкові функції не мають \"arguments\"
"
Якщо ми викличемо `arguments` об'єкта всередині стрíлкової функції, він візьме їх із зовнішньої "звичайної" функції.
Ось приклад:
function f() {
let showArg = () => alert(arguments[0]);
showArg();
}
f(1); // 1
Як ми пам’ятаємо, стрíлкові функції не мають власних this
. Тепер ми знаємо, що у них також немає особливого об'єкту arguments
.
## Синтаксис розширення [#spread-syntax] (з англ. spread syntax)
Ми щойно побачили, як отримати масив зі списку параметрів.
Але іноді нам потрібно зробити зворотнє.
Наприклад, є вбудована функція [Math.max](mdn:js/Math/max) що повертає найбільше число зі списку:
```js run
alert( Math.max(3, 5, 1) ); // 5
```
Тепер припустімо, що у нас є масив `[3, 5, 1]`. Як нам викликати `Math.max` з цим?
Передати "як є" не вийде, бо `Math.max` очікує список числових аргументів, а не єдиний масив:
```js run
let arr = [3, 5, 1];
*!*
alert( Math.max(arr) ); // NaN
*/!*
```
І, звісно ж, ми не можемо вручну перераховувати елементи в коді `Math.max(arr[0], arr[1], arr[2])`, тому що ми можемо й не знати, скільки їх існує. Під час виконання нашого сценарію їх може бути багато, а може і не бути. Та й взагалі робити це вручну було б жахливо.
Нам допоможе *синтаксис розширення*! Він схожий на параметри залишку, також використовуються `...`, але працює все навпаки.
Коли `...arr` використовується в дужках при виклику функції, він "розширює" ітеровуваний об'єкт `arr` до списку аргументів.
Для `Math.max`:
```js run
let arr = [3, 5, 1];
alert( Math.max(...arr) ); // 5 (перетворює масив у список аргументів)
```
Таким чином, ми також можемо передати кілька ітерацій:
```js run
let arr1 = [1, -2, 3, 4];
let arr2 = [8, 3, -8, 1];
alert( Math.max(...arr1, ...arr2) ); // 8
```
Ми навіть можемо поєднати синтаксис розширення з нормальними значеннями:
```js run
let arr1 = [1, -2, 3, 4];
let arr2 = [8, 3, -8, 1];
alert( Math.max(1, ...arr1, 2, ...arr2, 25) ); // 25
```
Також синтаксис розширення можна використовувати для об’єднання масивів:
```js run
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)
```
У наведених вище прикладах для демонстрації синтаксису розширення ми використовували масив, але підходить будь-який ітеровуваний (перебираваний) об'єкт.
Наприклад, тут ми використовуємо синтаксис розширення, щоб перетворити рядок у масив символів:
```js run
let str = "Hello";
alert( [...str] ); // H,e,l,l,o
```
Синтаксис розширення під капотом працює з перебираваними об'єктами так само, як це робить `for..of`.
Отже, для рядка, `for..of` повертає символи й `...str` перетворюється в `"H","e","l","l","o"`. Список символів передається в ініціалізатор масиву `[...str]`.
Для цього конкретного завдання ми також могли б використовувати `Array.from`, бо він перетворює перебираваний об'єкт (то й же рядок, або щось інше) на масив:
```js run
let str = "Hello";
// Array.from перетворює перебираваний об'єкт в масив
alert( Array.from(str) ); // H,e,l,l,o
```
Результат такий самий як при `[...str]`.
Але між `Array.from(obj)` та `[...obj]` є тонка різниця:
- `Array.from` працює як з псевдомасивами, так і з перебираваними об'єктами.
- Синтаксис розширення працює тільки з перебираваними об'єктами.
Отже, якщо треба перетворити щось на масив, то `Array.from` буде більш універсальним.
## Створити копію масива/об’єкта
Пам'ятаєте ми раніше говорили про `Object.assign()` [в минулому розділі](info:object-copy#cloning-and-merging-object-assign)?
Те ж саме можна зробити і з синтаксисом розширення.
```js run
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
```
Зауважте, що те ж саме можна зробити, щоб зробити копію об’єкта:
```js run
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`.