Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix rest-parameters-spread (01-06-02) #709

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 44 additions & 44 deletions 1-js/06-advanced-functions/02-rest-parameters-spread/article.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,15 @@

- `Math.max(arg1, arg2, ..., argN)` -- повертає найбільший з аргументів.
- `Object.assign(dest, src1, ..., srcN)` -- копіює властивості з `src1..N` до `dest`.
- ...і багато ще.
- ...і багато інших.

У цьому розділі ми дізнаємось, як зробити те саме. А також, як передати масиви таким функціям, як параметри.
У цьому розділі ми дізнаємось, як зробити те саме в власній функції. А також, як передати таким функціям в якості параметрів масиви.

## Залишкові параметри `...`
## Залишкові параметри `...` (з англ. rest parameters)

Функцію можна викликати з будь-якою кількістю аргументів, незалежно від того, як вона визначена.
Функцію можна викликати з будь-якою кількістю аргументів, незалежно від того, як вона оголошена.

Як тут:
Як от тут:
```js run
function sum(a, b) {
return a + b;
Expand All @@ -23,9 +23,9 @@ function sum(a, b) {
alert(sum(1, 2, 3, 4, 5));
```

Помилки "надмірних" аргументів у цьому випадку не буде. Але, звичайно, будуть враховані лише перші два, тому результатом у коді вище є `3`.
Помилки із-за "надмірних" аргументів у цьому випадку не буде. Але, звісно ж, враховані будуть лише перші два, тому результатом у коді вище є `3`.

Решту параметрів можна включити до визначення функції за допомогою трьох крапок `...` що передують імені масиву, який їх міститиме. Точки буквально означають "зібрати решту параметрів у масив".
Решту параметрів можна включити до визначення функції за допомогою трьох крапок `...` і після них ім'я масив, в який вони передадуться. Точки буквально означають "зібрати решту параметрів у масив".

Наприклад, щоб зібрати всі аргументи в масив `args`:

Expand All @@ -43,7 +43,7 @@ alert( sumAll(1, 2) ); // 3
alert( sumAll(1, 2, 3) ); // 6
```

Ми можемо вибрати перші параметри як змінні, а зібрати у масив лише залишки.
Ми можемо вказати щоб перші параметри отримувались як аргументи, а в масив пішли всі інші.

У цьому прикладі перші два аргументи переходять у змінні, а решта -- в масив `titles`:

Expand All @@ -62,7 +62,7 @@ showName("Юлій", "Цезар", "Консул", "Полководець");
```

````warn header="Залишкові параметри повинні бути в кінці"
Залишкові параметри збирають усі залишені аргументи, тому наступне не має сенсу та викликає помилку:
Залишкові параметри збирають усі залишені аргументи, тому такий код не спрацює і викличе помилку:

```js
function f(arg1, ...rest, arg2) { // arg2 після ...rest ?!
Expand All @@ -75,7 +75,7 @@ function f(arg1, ...rest, arg2) { // arg2 після ...rest ?!

## Змінна "arguments"

Існує також спеціальний об’єкт, подібний масиву `arguments` який містить усі аргументи за їх індексом.
Існує також спеціальний псевдомасив, об'єкт `arguments`, який містить усі аргументи з їх індексами.

Наприклад:

Expand All @@ -85,27 +85,27 @@ function showName() {
alert(arguments[0]);
alert(arguments[1]);

// це повторюване
// for(let arg of arguments) alert(arg);
// він ітеровуваний (перебираваний)
// for(let arg of arguments) alert(arg); працюватиме
}

// показує: 2, Julius, Caesar
showName("Julius", "Caesar");
// показує: 2, Юлій, Цезар
showName("Юлій", "Цезар");

// показує: 1, Ilya, undefined (жодного другого аргументу)
showName("Ilya");
// показує: 1, Ілля, undefined (другого аргументу нема)
showName("Ілля");
```

За старих часів залишкові параметри не існували в мові, єдиний спосіб отримати всі аргументи функції був за допомогою `arguments`. І він все ще працює, ми можемо знайти його в старому коді.
В стародавні часи такої можливості як залишкові параметри в JavaScript не існувало. Тому єдиним способом отримати всі аргументи функції було за допомогою `arguments`. І він все ще працює, ми можемо знайти його в старому коді.

Але недоліком є те, що хоча `arguments` є одночасно масивом і ітерабельним, це не масив. Він не підтримує методи масиву, тому ми не можемо наприклад викликати `arguments.map(...)`.
Але недоліком є те, що хоч `arguments` є одночасно і псевдомасивом, і ітеровуваним (перебираваним) об'єктом, та все ж це об'єкт, а не масив. Він не підтримує методи масиву, тому ми не можемо наприклад викликати `arguments.map(...)`.

Крім того, він завжди містить усі аргументи. Ми не можемо отримати їх частково, як це було зроблено з параметрами залишку.
Крім того, він завжди містить усі аргументи. Ми не можемо отримати лише частину з них, як це можна робити з допомогою залишкових параметрів.

Тому, коли нам потрібні вбудовані методи, тоді краще використати параметри залишку.
Тому, коли нам потрібні вбудовані методи масивів, тоді краще використати залишкові параметри.

````smart header="Стрілочні функції не мають `\"arguments\"`"
Якщо ми викличемо `arguments` об'єкта із стрілочною функцією, він візьме їх із зовнішньої "звичайної" функції.
````smart header="Стрíлкові функції не мають `\"arguments\"`"
Якщо ми викличемо `arguments` об'єкта всередині стрíлкової функції, він візьме їх із зовнішньої "звичайної" функції.

Ось приклад:

Expand All @@ -118,13 +118,13 @@ function f() {
f(1); // 1
```

Як ми пам’ятаємо, стрілочні функції не мають власних `this`. Тепер ми знаємо, що у них також немає особливого об'єкту `arguments`.
Як ми пам’ятаємо, стрíлкові функції не мають власних `this`. Тепер ми знаємо, що у них також немає особливого об'єкту `arguments`.
````


## Синтаксис розширення [#spread-syntax]
## Синтаксис розширення [#spread-syntax] (з англ. spread syntax)

Ми тільки що побачили, як отримати масив зі списку параметрів.
Ми щойно побачили, як отримати масив зі списку параметрів.

Але іноді нам потрібно зробити зворотнє.

Expand All @@ -134,7 +134,7 @@ f(1); // 1
alert( Math.max(3, 5, 1) ); // 5
```

Тепер припустимо, що у нас є масив `[3, 5, 1]`. Як нам викликати `Math.max` з цим?
Тепер припустімо, що у нас є масив `[3, 5, 1]`. Як нам викликати `Math.max` з цим?

Передати "як є" не вийде, бо `Math.max` очікує список числових аргументів, а не єдиний масив:

Expand All @@ -146,11 +146,11 @@ alert( Math.max(arr) ); // NaN
*/!*
```

І, звісно ж, ми не можемо вручну перераховувати елементи в коді `Math.max(arr[0], arr[1], arr[2])`, тому що ми можемо не знати, скільки їх існує. Під час виконання нашого сценарію їх може бути багато, а може і не бути. І це було б потворно.
І, звісно ж, ми не можемо вручну перераховувати елементи в коді `Math.max(arr[0], arr[1], arr[2])`, тому що ми можемо й не знати, скільки їх існує. Під час виконання нашого сценарію їх може бути багато, а може і не бути. Та й взагалі робити це вручну було б жахливо.

Нам допоможе *синтаксис розширення*! Він схожий на параметри залишку, також використовуються `...`, але працює все навпаки.

Коли `...arr` використовується у виклику функції, він "розширює" повторюванний об'єкт `arr` до списку аргументів.
Коли `...arr` використовується в дужках при виклику функції, він "розширює" ітеровуваний об'єкт `arr` до списку аргументів.

Для `Math.max`:

Expand Down Expand Up @@ -189,10 +189,10 @@ let arr2 = [8, 9, 15];
let merged = [0, ...arr, 2, ...arr2];
*/!*

alert(merged); // 0,3,5,1,2,8,9,15 (0, then arr, then 2, then arr2)
alert(merged); // 0,3,5,1,2,8,9,15 (0, тоді arr, тоді 2, тоді arr2)
```

У наведених вище прикладах ми використовували масив для демонстрації синтаксису розширення, але підходить будь-який повторюванний об'єкт.
У наведених вище прикладах для демонстрації синтаксису розширення ми використовували масив, але підходить будь-який ітеровуваний (перебираваний) об'єкт.

Наприклад, тут ми використовуємо синтаксис розширення, щоб перетворити рядок у масив символів:

Expand All @@ -202,32 +202,32 @@ let str = "Hello";
alert( [...str] ); // H,e,l,l,o
```

Синтаксис розширення внутрішньо використовує ітератори для збору елементів так само, як це робить `for..of`.
Синтаксис розширення під капотом працює з перебираваними об'єктами так само, як це робить `for..of`.

Отже, для рядка, `for..of` повертає символи так само `...str` повертає `"H","e","l","l","o"`. Список символів передається в ініціалізатор масиву `[...str]`.
Отже, для рядка, `for..of` повертає символи й `...str` перетворюється в `"H","e","l","l","o"`. Список символів передається в ініціалізатор масиву `[...str]`.

Для цього конкретного завдання ми також могли б використовувати `Array.from`, тому що він перетворює ітерабельний об'єкт (як рядок) на масив:
Для цього конкретного завдання ми також могли б використовувати `Array.from`, бо він перетворює перебираваний об'єкт (то й же рядок, або щось інше) на масив:

```js run
let str = "Hello";

// Array.from перетворює ітерабельний об'єкт в масив
// Array.from перетворює перебираваний об'єкт в масив
alert( Array.from(str) ); // H,e,l,l,o
```

Результат такий самий як `[...str]`.
Результат такий самий як при `[...str]`.

Але між `Array.from(obj)` та `[...obj]` є тонка різниця:

- `Array.from` працює як з масиво-подібними об'єктами, так і з об'єктами, що ітеруються.
- Синтаксис розширення працює тільки з ітераторами.
- `Array.from` працює як з псевдомасивами, так і з перебираваними об'єктами.
- Синтаксис розширення працює тільки з перебираваними об'єктами.

Отже, для завдання перетворити щось на масив `Array.from` буде більш універсальним.
Отже, якщо треба перетворити щось на масив, то `Array.from` буде більш універсальним.


## Створити копію масива/об’єкта

Пригадайте, коли ми говорили `Object.assign()` [в минулому розділі](info:object-copy#cloning-and-merging-object-assign)?
Пам'ятаєте ми раніше говорили про `Object.assign()` [в минулому розділі](info:object-copy#cloning-and-merging-object-assign)?

Те ж саме можна зробити і з синтаксисом розширення.

Expand Down Expand Up @@ -273,23 +273,23 @@ 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)` тому ми вважаємо за краще використовувати його, коли це можливо.
Цей спосіб копіювання об’єкта набагато коротший, ніж `let objCopy = Object.assign({}, obj)` чи для масиву `let arrCopy = Object.assign([], arr)`. Тому ми вважаємо за краще використовувати його, коли це можливо.


## Підсумки

Коли ми бачимо `"..."` у коді це або залишкові параметри, або синтаксис розширення.
Коли ми бачимо `"..."` у коді - це або залишкові параметри, або синтаксис розширення.

Існує простий спосіб відрізнити їх:

- Коли `...` находиться в кінці параметрів функції, це "залишкові параметри" і він збирає решту переданих аргументів у масив.
- Коли `...` знаходиться в кінці параметрів функції, це "залишкові параметри" і він збирає решту переданих аргументів у масив.
- Коли `...` виникає під час виклику функції чи чогось подібного, це називається "синтаксисом розширення" і розширює масив у список.

Використовуйте паттерни:
Паттерни використання:

- Залишкові параметри використовуються для створення функцій, які приймають будь-яку кількість аргументів.
- Синтаксис розширення використовується для передачі масиву у функції, які зазвичай вимагають список із багатьох аргументів.

Разом вони допомагають легко переміщатися між списком та масивом параметрів.

Усі аргументи виклику функції також доступні в "олдскульному" ітерабельному об’єкті `arguments`.
Усі аргументи виклику функції також доступні в "олдскульному" ітеровуваному об’єкті `arguments`.