Parametry zbytku a syntaxe šíření

Mnoho vestavěných funkcí JavaScriptu podporuje libovolný počet argumentů.

Například:

  • Math.max(arg1, arg2, ..., argN) – vrátí největší z argumentů.
  • Object.assign(dest, src1, ..., srcN) – zkopíruje vlastnosti z src1..N do dest .
  • …a tak dále.

V této kapitole se naučíme, jak udělat totéž. A také, jak předat pole takovým funkcím, jako jsou parametry.

Parametry zbytku ...

Funkci lze volat s libovolným počtem argumentů, bez ohledu na to, jak je definována.

Jako zde:

function sum(a, b) {
 return a + b;
}

alert( sum(1, 2, 3, 4, 5) );

Nedojde k žádné chybě kvůli „nadměrným“ argumentům. Ale do výsledku se samozřejmě započítají pouze první dva.

Zbytek parametrů lze zahrnout do definice funkce pomocí tří teček ... následovaný názvem pole, které je bude obsahovat. Tečky doslova znamenají „shromáždit zbývající parametry do pole“.

Například shromáždit všechny argumenty do pole args :

function sumAll(...args) { // args is the name for the array
 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

Můžeme se rozhodnout získat první parametry jako proměnné a shromáždit pouze zbytek.

Zde první dva argumenty jdou do proměnných a zbytek do titles pole:

function showName(firstName, lastName, ...titles) {
 alert( firstName + ' ' + lastName ); // Julius Caesar

 // the rest go into titles array
 // i.e. titles = ["Consul", "Imperator"]
 alert( titles[0] ); // Consul
 alert( titles[1] ); // Imperator
 alert( titles.length ); // 2
}

showName("Julius", "Caesar", "Consul", "Imperator");
Zbývající parametry musí být na konci

Parametry zbytku shromažďují všechny zbývající argumenty, takže následující nedává smysl a způsobuje chybu:

function f(arg1, ...rest, arg2) { // arg2 after ...rest ?!
 // error
}

...rest musí být vždy poslední.

Proměnná „argumenty“

Existuje také speciální objekt podobný poli s názvem arguments který obsahuje všechny argumenty podle jejich indexu.

Například:

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

 // it's iterable
 // for(let arg of arguments) alert(arg);
}

// shows: 2, Julius, Caesar
showName("Julius", "Caesar");

// shows: 1, Ilya, undefined (no second argument)
showName("Ilya");

Za starých časů parametry odpočinku v jazyce a pomocí arguments neexistovaly byl jediný způsob, jak získat všechny argumenty funkce. A stále to funguje, najdeme to ve starém kódu.

Ale nevýhodou je, že ačkoli arguments je jak pole podobné, tak iterovatelné, není to pole. Nepodporuje metody pole, takže nemůžeme volat arguments.map(...) například.

Také vždy obsahuje všechny argumenty. Nemůžeme je zachytit částečně, jako jsme to udělali s parametry odpočinku.

Takže když potřebujeme tyto funkce, pak jsou preferovány ostatní parametry.

Funkce šipek nemají "arguments"

Pokud přistoupíme k arguments objekt z funkce šipky, převezme je z vnější „normální“ funkce.

Zde je příklad:

function f() {
 let showArg = () => alert(arguments[0]);
 showArg();
}

f(1); // 1

Jak si pamatujeme, funkce šipek nemají vlastní this . Nyní víme, že nemají speciální arguments objekt buď.

Spread syntaxe

Právě jsme viděli, jak získat pole ze seznamu parametrů.

Někdy ale musíme udělat pravý opak.

Například existuje vestavěná funkce Math.max, která vrací nejvyšší číslo ze seznamu:

alert( Math.max(3, 5, 1) ); // 5

Nyní řekněme, že máme pole [3, 5, 1] . Jak nazýváme Math.max s tím?

Předání „tak jak je“ nebude fungovat, protože Math.max očekává seznam číselných argumentů, nikoli jedno pole:

let arr = [3, 5, 1];

alert( Math.max(arr) ); // NaN

A určitě nemůžeme ručně vypsat položky v kódu Math.max(arr[0], arr[1], arr[2]) , protože si možná nejsme jisti, kolik jich je. Jak se náš skript spouští, může toho být hodně nebo nemusí být žádný. A to by bylo ošklivé.

Spread syntaxe na záchranu! Vypadá podobně jako parametry odpočinku, také pomocí ... , ale dělá pravý opak.

Když ...arr se používá ve volání funkce, „rozbalí“ iterovatelný objekt arr do seznamu argumentů.

Pro Math.max :

let arr = [3, 5, 1];

alert( Math.max(...arr) ); // 5 (spread turns array into a list of arguments)

Tímto způsobem také můžeme předat více iterovatelných:

let arr1 = [1, -2, 3, 4];
let arr2 = [8, 3, -8, 1];

alert( Math.max(...arr1, ...arr2) ); // 8

Můžeme dokonce kombinovat syntaxi spreadu s normálními hodnotami:

let arr1 = [1, -2, 3, 4];
let arr2 = [8, 3, -8, 1];

alert( Math.max(1, ...arr1, 2, ...arr2, 25) ); // 25

Syntaxi spread lze také použít ke sloučení polí:

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, then arr, then 2, then arr2)

Ve výše uvedených příkladech jsme použili pole k demonstraci syntaxe spreadu, ale bude stačit jakákoli iterovatelná.

Například zde používáme syntaxi spread k přeměně řetězce na pole znaků:

let str = "Hello";

alert( [...str] ); // H,e,l,l,o

Syntaxe spreadu interně používá ke shromažďování prvků iterátory, stejně jako for..of ano.

Takže pro řetězec for..of vrátí znaky a ...str se změní na "H","e","l","l","o" . Seznam znaků je předán inicializátoru pole [...str] .

Pro tento konkrétní úkol bychom také mohli použít Array.from , protože převádí iterovatelné (jako řetězec) na pole:

let str = "Hello";

// Array.from converts an iterable into an array
alert( Array.from(str) ); // H,e,l,l,o

Výsledek je stejný jako [...str] .

Mezi Array.from(obj) je však nepatrný rozdíl a [...obj] :

  • Array.from funguje jak na polích podobných, tak na iterovatelných.
  • Syntaxe šíření funguje pouze s iterovatelnými.

Takže pro úkol převést něco na pole Array.from bývá univerzálnější.

Zkopírujte pole/objekt

Pamatujte, když jsme mluvili o Object.assign() v minulosti?

Je možné udělat totéž se syntaxí spreadu.

let arr = [1, 2, 3];

let arrCopy = [...arr]; // spread the array into a list of parameters
 // then put the result into a new array

// do the arrays have the same contents?
alert(JSON.stringify(arr) === JSON.stringify(arrCopy)); // true

// are the arrays equal?
alert(arr === arrCopy); // false (not same reference)

// modifying our initial array does not modify the copy:
arr.push(4);
alert(arr); // 1, 2, 3, 4
alert(arrCopy); // 1, 2, 3

Všimněte si, že je možné udělat totéž pro vytvoření kopie objektu:

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

let objCopy = { ...obj }; // spread the object into a list of parameters
 // then return the result in a new object

// do the objects have the same contents?
alert(JSON.stringify(obj) === JSON.stringify(objCopy)); // true

// are the objects equal?
alert(obj === objCopy); // false (not same reference)

// modifying our initial object does not modify the copy:
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}

Tento způsob kopírování objektu je mnohem kratší než let objCopy = Object.assign({}, obj) nebo pro pole let arrCopy = Object.assign([], arr) proto jej raději používáme, kdykoli je to možné.

Shrnutí

Když vidíme "..." v kódu jsou to buď zbývající parametry, nebo syntaxe spreadu.

Existuje snadný způsob, jak je rozlišit:

  • Když ... je na konci parametrů funkce, jsou to „zbytkové parametry“ a shromažďuje zbytek seznamu argumentů do pole.
  • Když ... vyskytuje se ve volání funkce nebo podobně, nazývá se to „spread syntax“ a rozšiřuje pole do seznamu.

Použijte vzory:

  • Zbývající parametry se používají k vytváření funkcí, které přijímají libovolný počet argumentů.
  • Syntaxe spread se používá k předání pole funkcím, které normálně vyžadují seznam mnoha argumentů.

Společně pomáhají snadno cestovat mezi seznamem a řadou parametrů.

Všechny argumenty volání funkce jsou také dostupné ve „starém“ arguments :iterovatelný objekt podobný poli.