From 0975eb6254ba1db31c66c078659e2b4c1ef7c1ab Mon Sep 17 00:00:00 2001 From: Mykola Myslovskyi Date: Tue, 7 Jun 2022 23:15:03 +0300 Subject: [PATCH] translation(JS): /docs/Web/JavaScript/Reference/Operators/Spread_syntax (#331) * translation(JS): /docs/Web/JavaScript/Reference/Operators/Spread_syntax * Apply suggestions from code review Co-authored-by: Vitalii Perehonchuk Co-authored-by: Vitalii Perehonchuk --- .../operators/spread_syntax/index.md | 304 ++++++++++++++++++ 1 file changed, 304 insertions(+) create mode 100644 files/uk/web/javascript/reference/operators/spread_syntax/index.md diff --git a/files/uk/web/javascript/reference/operators/spread_syntax/index.md b/files/uk/web/javascript/reference/operators/spread_syntax/index.md new file mode 100644 index 0000000000..646dc1cbcf --- /dev/null +++ b/files/uk/web/javascript/reference/operators/spread_syntax/index.md @@ -0,0 +1,304 @@ +--- +title: Синтаксис розгортання (...) +slug: Web/JavaScript/Reference/Operators/Spread_syntax +tags: + - ECMAScript 2015 + - Iterator + - JavaScript + - Language feature + - Reference +browser-compat: javascript.operators.spread +--- +{{jsSidebar("Operators")}} + +**Синтаксис розгортання** (`...`) дає змогу ітерованим елементам (таким, як вираз із масивом чи рядок) розгортатися в тих місцях, де очікується нуль чи більше аргументів (у викликах функцій) чи елементів (для літералів масиву), або ж об'єктам розгортатися в місцях, де очікуються нуль чи більше пар ключ-значення (в об'єктних літералах). + +{{EmbedInteractiveExample("pages/js/expressions-spreadsyntax.html")}} + +## Опис + +Синтаксис розгортання можна використати у випадку, коли всі елементи з об'єкта чи масиву потрібно внести в певного роду список. + +У наведеному вище прикладі, визначена функція приймає аргументи `x`, `y`, та `z`, і повертає суму цих значень. Також задано значення масиву. + +При викликанні функції всі значення з масиву передаються в неї шляхом вказування синтаксису розгортання та назви масиву — `...numbers`. + +Якби масив містив більш ніж три числа (наприклад `[1, 2, 3, 4]`) — це все ще працювало б. За винятком того, що у функцію передавалися б всі 4 числа, хоча використовувалися б лише перші три, хіба що до функції додалося би більше аргументів, як от: + +```js +function sum(x, y, z, n) { + return x + y + z + n; +} +``` + +Наведений вище приклад є дещо негнучким; справжня цінність синтаксису розгортання полягає в тому, що він працює з тим самим значенням, незалежно від того, скільки елементів зберігається в об'єкті, масиві тощо. + +Він зазвичай використовується тоді, коли потрібно додати новий елемент до місцевого сховища даних, або показати всі збережені значення разом із новим додатковим елементом. Дуже простий приклад операції цього типу може мати такий вигляд: + +```js +let numberStore = [0, 1, 2]; +let newNumber = 12; +numberStore = [...numberStore, newNumber]; +``` + +У наведеному вище прикладі останній рядок можна перезапускати стільки разів, скільки заманеться, продовжуючи додавати нові числа 12 в кінці масиву. + +## Синтаксис + +Для викликів функцій: + +```js +myFunction(...iterableObj); // передати всі елементи об'єкта iterableObj аргументами до функції myFunction +``` + +Для літералів масиву: + +```js +[...iterableObj, '4', 'п\'ять', 6]; // об'єднати два масиви шляхом вставки всіх елементів із iterableObj +``` + +Для об'єктних літералів (новинка ECMAScript 2018): + +```js +let objClone = { ...obj }; // передати всі пари ключ:значення з об'єкта +``` + +## Синтаксис решти (параметрів) + +Синтаксис решти має точнісінько такий самий вигляд, як синтаксис розгортання. В певному сенсі, синтаксис решти — це протилежність синтаксису розгортання. Синтаксис розгортання "розкриває" масив у сукупність його елементів, позаяк синтаксис решти збирає декілька елементів і "стискає" їх в один елемент. Більше про це в розділі {{jsxref("Functions/rest_parameters", "решта параметрів", "", 1)}}. + +## Приклади + +### Розгортання у викликах функцій + +#### Заміна apply() + +Зазвичай у тих випадках, де потрібно передати елементи масиву як аргументи до функції, використовують {{jsxref("Function.prototype.apply()")}}. + +```js +function myFunction(x, y, z) { } +let args = [0, 1, 2]; +myFunction.apply(null, args); +``` + +За допомогою синтаксису розгортання наведений вище приклад можна записати так: + +```js +function myFunction(x, y, z) { } +let args = [0, 1, 2]; +myFunction(...args); +``` + +Будь-який аргумент зі списку може використовувати синтаксис розгортання. Також цей синтаксис можна вживати декілька разів. + +```js +function myFunction(v, w, x, y, z) { } +let args = [0, 1]; +myFunction(-1, ...args, 2, ...[3]); +``` + +#### Застосування до оператора `new` + +Під час виклику конструктора з оператором {{jsxref("Operators/new", "new")}} не можна **безпосередньо** використати масив і `apply()` (функція `apply()` виконує `[[Call]]`, а не `[[Construct]]`). Однак, завдяки синтаксису розгортання можна легко вжити оператор `new` із масивом: + +```js +let dateFields = [1970, 0, 1]; // 1 Січня 1970 +let d = new Date(...dateFields); +``` + +Аби вжити оператор `new` із масивом параметрів без синтаксису розгортання, довелося б робити це **опосередковано**, шляхом часткового застосування: + +```js +function applyAndNew(constructor, args) { + function partial () { + return constructor.apply(this, args); + }; + if (typeof constructor.prototype === "object") { + partial.prototype = Object.create(constructor.prototype); + } + return partial; +} + +function myConstructor () { + console.log("arguments.length: " + arguments.length); + console.log(arguments); + this.prop1="val1"; + this.prop2="val2"; +}; + +let myArguments = ["привіт", "як", "ся", "маєте", "шановний", null]; +let myConstructorWithArguments = applyAndNew(myConstructor, myArguments); + +console.log(new myConstructorWithArguments); +// (internal log of myConstructor): arguments.length: 6 +// (internal log of myConstructor): ["привіт", "як", "ся", "маєте", "шановний", null] +// (log of "new myConstructorWithArguments"): {prop1: "val1", prop2: "val2"} +``` + +### Оператор розгортання в літералах масивів + +#### Потужніший літерал масиву + +Аби створити новий масив без синтаксису розгортання, використавши вже наявний масив для його часткового наповнення, вже недостатньо просто літерала масиву. Натомість доведеться застосувати імперативний код в комбінації з {{jsxref("Array.prototype.push", "push()")}}, {{jsxref("Array.prototype.splice", "splice()")}}, {{jsxref("Array.prototype.concat", "concat()")}} тощо. Синтаксис розгортання дає змогу зробити це значно ефективніше: + +```js +let parts = ['плечі', 'коліна']; +let lyrics = ['голова', ...parts, 'й', 'пальці']; +// [ "голова", "плечі", "коліна", "й", "пальці" ] +``` + +Точнісінько як для розгортання списку аргументів, `...` можна вживати будь-де всередині масиву, і робити це більш ніж однократно. + +#### Копіювання масиву + +```js +let arr = [1, 2, 3]; +let arr2 = [...arr]; // так само, як arr.slice() + +arr2.push(4); +// arr2 стає [1, 2, 3, 4] +// arr залишається незміненим +``` + +> **Примітка:** Синтаксис розгортання проходить на один рівень вглиб масиву під час копіювання. У зв'язку з чим він може не підходити для копіювання багатовимірних масивів, як показує наступний приклад. (Те саме стосується комбінації синтаксису розгортання із {{jsxref("Object.assign()")}}.) +> +> ```js example-bad +> let a = [[1], [2], [3]]; +> let b = [...a]; +> +> b.shift().shift(); +> // 1 +> +> // ОЙ лишенько! Це також вплинуло і на масив 'a': +> a +> // [[], [2], [3]] +> ``` + +#### Кращий спосіб з'єднання масивів + +{{jsxref("Array.prototype.concat()")}} часто вживається для приєднання масиву до кінця вже наявного масиву. Без застосування синтаксису розгортання це можна зробити так: + +```js +let arr1 = [0, 1, 2]; +let arr2 = [3, 4, 5]; + +// Додати всі елементи масиву arr2 у масив arr1 +arr1 = arr1.concat(arr2); +``` + +З появою синтаксису розгортання це перетворилося на: + +```js +let arr1 = [0, 1, 2]; +let arr2 = [3, 4, 5]; + +arr1 = [...arr1, ...arr2]; +// arr1 тепер дорівнює [0, 1, 2, 3, 4, 5] +// Примітка: Не для застосування із const, інакше код викине помилку TypeError (недійсне присвоєння) +``` + +{{jsxref("Array.prototype.unshift()")}} часто вживається для приєднання масиву до початку вже наявного масиву. Без застосування синтаксису розгортання це можна зробити так: + +```js +let arr1 = [0, 1, 2]; +let arr2 = [3, 4, 5]; + +// Приєднати всі елементи масиву arr2 на початку arr1 +Array.prototype.unshift.apply(arr1, arr2) + +// arr1 тепер дорівнює [3, 4, 5, 0, 1, 2] +``` + +З появою синтаксису розгортання це перетворилося на: + +```js +let arr1 = [0, 1, 2]; +let arr2 = [3, 4, 5]; + +arr1 = [...arr2, ...arr1]; +// arr1 тепер дорівнює [3, 4, 5, 0, 1, 2] +``` + +> **Примітка:** На відміну від `unshift()`, цей код створює новий `arr1`, а не модифікує початковий масив `arr1` на місці. + +### Розгортання в об'єктних літералах + +Проєкт [Оператор решти/розгортання для ECMAScript (англ.)](https://github.com/tc39/proposal-object-rest-spread) (ES2018) додав функціонал розгортання властивостей до {{jsxref("Operators/Object_initializer", "об'єктних літералів", 1)}}. Він копіює власні перелічувані властивості з переданого об'єкта у новий. + +Тепер можливо виконувати поверхневе клонування (тобто за винятком прототипу) чи злиття об'єктів за допомогою коротшого синтаксису, аніж {{jsxref("Object.assign()")}}. + +```js +let obj1 = { foo: 'bar', x: 42 }; +let obj2 = { foo: 'baz', y: 13 }; + +let clonedObj = { ...obj1 }; +// Об'єкт { foo: "bar", x: 42 } + +let mergedObj = { ...obj1, ...obj2 }; +// Об'єкт { foo: "baz", x: 42, y: 13 } +``` + +Зауважте, що {{jsxref("Object.assign()")}} може застосовуватись для модифікації об'єкта, а от синтаксис розгортання — ні. + +```js +const objectAssign = Object.assign({ set foo(val) { console.log(val); } }, { foo: 1 }); +// Друкує "1"; objectAssign.foo досі містить первинний сеттер + +const spread = { set foo(val) { console.log(val); }, ...{ foo: 1 } }; +// Нічого не друкується; spread.foo дорівнює 1 +``` + +Неможливо в нативному коді повторно реалізувати функцію {{jsxref("Object.assign()")}} за допомогою одного лише оператора розгортання: + +```js +const obj1 = { foo: 'bar', x: 42 }; +const obj2 = { foo: 'baz', y: 13 }; +const merge = (...objects) => ({ ...objects }); + +const mergedObj1 = merge(obj1, obj2); +// Об'єкт { 0: { foo: 'bar', x: 42 }, 1: { foo: 'baz', y: 13 } } + +const mergedObj2 = merge({}, obj1, obj2); +// Об'єкт { 0: {}, 1: { foo: 'bar', x: 42 }, 2: { foo: 'baz', y: 13 } } +``` + +У наведеному вище прикладі синтаксис розгортання працює не так, як можна було очікувати: він розгортає _масив_ аргументів у об'єктний літерал, через параметр решти. Нижче – інша реалізація функції `merge` із застосуванням оператора розгортання, поведінка якої аналогічна до {{jsxref("Object.assign()")}}, за винятком того, що вона не змушує спрацьовувати сеттери і не модифікує жодного об'єкта: + +```js +const obj1 = { foo: 'bar', x: 42 }; +const obj2 = { foo: 'baz', y: 13 }; +const merge = (...objects) => objects.reduce((acc, cur) => ({ ...acc, ...cur })); + +const mergedObj1 = merge(obj1, obj2); +// Об'єкт { foo: 'baz', x: 42, y: 13 } +``` + +### Лише для ітерованих об'єктів + +Синтаксис розгортання (за винятком тих випадків, коли мова йде про розгортання властивостей) можна застосовувати лише до [ітерованих](/uk/docs/Web/JavaScript/Reference/Global_Objects/Symbol/iterator) об'єктів, як от {{jsxref("Array")}}, або з функціями перебирання, такими як `map()`, `reduce()`, та `assign()`. + +Багато об'єктів не є ітерованими, включно із {{JSxRef("Object")}}: + +```js +let obj = {'key1': 'value1'}; +let array = [...obj]; // TypeError: obj is not iterable +``` + +Для застосування синтаксису розгортання із такими об'єктами необхідно спершу надати функцію ітератора. + +### Розгортання зі множинними значеннями + +Під час застосування синтаксису розгортання у викликах функцій слід мати на увазі можливість перевищення обмеження кількості аргументів у рушії JavaScript. Подробиці дивіться у розділі {{jsxref("Function.prototype.apply", "apply()")}}. + +## Специфікації + +{{Specifications}} + +## Сумісність із браузерами + +{{Compat}} + +## Дивіться також + +- {{jsxref("Functions/rest_parameters", "Решта параметрів", "", 1)}}, (також '`...`') +- {{jsxref("Function.prototype.apply()")}} (також '`...`')