Многие встроенные функции JavaScript поддерживают произвольное количество аргументов.
Например:
Math.max(arg1, arg2, ..., argN)
– возвращает наибольший из аргументов.
Object.assign(dest, src1, ..., srcN)
– копирует свойства из src1..N
в dest
.
…и так далее.
В этой главе мы научимся делать то же самое. А также, как передавать массивы таким функциям как параметры.
...
Функцию можно вызывать с любым количеством аргументов, независимо от того, как она определена.
Как здесь:
функция sum(a, b) { вернуть а + б; } Предупреждение(сумма(1, 2, 3, 4, 5));
Ошибки из-за «лишних» аргументов не будет. Но, конечно, в результате будут учтены только первые два, поэтому результат в приведенном выше коде равен 3
.
Остальные параметры можно включить в определение функции, используя три точки ...
за которыми следует имя массива, который их будет содержать. Точки буквально означают «собрать оставшиеся параметры в массив».
Например, чтобы собрать все аргументы в массив args
:
function sumAll(...args) { // args — имя массива пусть сумма = 0; for (пусть arg из args) sum += arg; сумма возврата; } Предупреждение(суммаВсе(1)); // 1 Предупреждение(суммаВсе(1, 2)); // 3 Предупреждение(суммаВсе(1, 2, 3)); // 6
Мы можем выбрать получение первых параметров в качестве переменных и собирать только остальные.
Здесь первые два аргумента переходят в переменные, а остальные — в массив titles
:
функция showName(firstName, LastName, ...titles) { Предупреждение (имя + '' + последнее имя); // Юлий Цезарь // остальное попадает в массив заголовков // т.е. titles = ["Консул", "Император"] предупреждение(заголовки[0]); // Консул предупреждение(заголовки[1]); // Император оповещение(titles.length); // 2 } showName("Юлий", "Цезарь", "Консул", "Император");
Остальные параметры должны быть в конце
Остальные параметры собирают все оставшиеся аргументы, поэтому следующее не имеет смысла и вызывает ошибку:
function f(arg1, ...rest, arg2) { // arg2 после ...rest ?! // ошибка }
...rest
всегда должно быть последним.
Существует также специальный объект, похожий на массив, с именем arguments
, который содержит все аргументы по их индексу.
Например:
функция showName() { предупреждение(аргументы.длина); предупреждение(аргументы[0]); предупреждение(аргументы[1]); // это можно повторить // for(пусть аргументы) alert(arg); } // показывает: 2, Юлий, Цезарь showName("Юлий", "Цезарь"); // показывает: 1, Илья, неопределенно (без второго аргумента) showName("Илья");
Раньше в языке не существовало остальных параметров, и использование arguments
было единственным способом получить все аргументы функции. И это до сих пор работает, мы можем найти это в старом коде.
Но недостатком является то, что, хотя arguments
и похожи на массивы, и повторяемы, это не массив. Он не поддерживает методы массива, поэтому мы не можем, например, вызвать arguments.map(...)
.
Кроме того, он всегда содержит все аргументы. Мы не можем захватить их частично, как мы это сделали с остальными параметрами.
Поэтому, когда нам нужны эти функции, предпочтительны остальные параметры.
Стрелочные функции не имеют "arguments"
Если мы получаем доступ к объекту arguments
из стрелочной функции, он берет их из внешней «обычной» функции.
Вот пример:
функция е() { let showArg = () => alert(arguments[0]); показатьАрг(); } е(1); // 1
Как мы помним, стрелочные функции не имеют собственного this
. Теперь мы знаем, что у них также нет объекта специальных arguments
.
Мы только что увидели, как получить массив из списка параметров.
Но иногда нам нужно сделать ровно обратное.
Например, есть встроенная функция Math.max, которая возвращает наибольшее число из списка:
предупреждение( Math.max(3, 5, 1)); // 5
Теперь предположим, что у нас есть массив [3, 5, 1]
. Как нам с его помощью вызвать Math.max
?
Передача его «как есть» не сработает, поскольку Math.max
ожидает список числовых аргументов, а не один массив:
пусть arr = [3, 5, 1]; предупреждение(Math.max(arr)); // НЭН
И, конечно, мы не можем вручную перечислять элементы в коде Math.max(arr[0], arr[1], arr[2])
, потому что мы можем быть не уверены, сколько их. Во время выполнения нашего скрипта их может быть много, а может и не быть. И это было бы некрасиво.
Распространите синтаксис в помощь! Он похож на параметры rest, также используя ...
, но действует совершенно наоборот.
Когда ...arr
используется в вызове функции, он «расширяет» итерируемый объект arr
в список аргументов.
Для Math.max
:
пусть arr = [3, 5, 1]; предупреждение( Math.max(...arr)); // 5 (разворот превращает массив в список аргументов)
Мы также можем передать несколько итераций таким образом:
пусть arr1 = [1, -2, 3, 4]; пусть arr2 = [8, 3, -8, 1]; alert( Math.max(...arr1, ...arr2)); // 8
Мы даже можем объединить синтаксис расширения с обычными значениями:
пусть arr1 = [1, -2, 3, 4]; пусть arr2 = [8, 3, -8, 1]; alert( Math.max(1, ...arr1, 2, ...arr2, 25) ); // 25
Кроме того, синтаксис распространения можно использовать для объединения массивов:
пусть arr = [3, 5, 1]; пусть arr2 = [8, 9, 15]; let merged = [0, ...arr, 2, ...arr2]; оповещение (объединено); // 0,3,5,1,2,8,9,15 (0, затем Arr, затем 2, затем Arr2)
В приведенных выше примерах мы использовали массив для демонстрации синтаксиса распространения, но подойдет любая итерация.
Например, здесь мы используем синтаксис расширения, чтобы превратить строку в массив символов:
let str = "Привет"; предупреждение([...стр]); // Привет
Синтаксис расширения внутренне использует итераторы для сбора элементов, так же, как и for..of
.
Итак, для строки for..of
возвращает символы, а ...str
становится "H","e","l","l","o"
. Список символов передается инициализатору массива [...str]
.
Для этой конкретной задачи мы также могли бы использовать Array.from
, поскольку он преобразует итерируемый объект (например, строку) в массив:
let str = "Привет"; // Array.from преобразует итерируемый объект в массив предупреждение(Array.from(str)); // Привет
Результат тот же, что и [...str]
.
Но между Array.from(obj)
и [...obj]
есть небольшая разница:
Array.from
работает как с массивами, так и с итерациями.
Синтаксис распространения работает только с итерируемыми объектами.
Итак, для задачи преобразования чего-либо в массив Array.from
имеет тенденцию быть более универсальным.
Помните, мы раньше говорили об Object.assign()
?
То же самое можно сделать и с синтаксисом распространения.
пусть arr = [1, 2, 3]; пусть arrCopy = [...arr]; // раскладываем массив в список параметров // затем помещаем результат в новый массив // имеют ли массивы одинаковое содержимое? alert(JSON.stringify(arr) === JSON.stringify(arrCopy)); // истинный // массивы равны? Предупреждение (arr === arrCopy); // ложь (не та же ссылка) // изменение нашего исходного массива не приводит к изменению копии: arr.push(4); оповещение (прибытие); // 1, 2, 3, 4 оповещение (arrCopy); // 1, 2, 3
Обратите внимание, что то же самое можно сделать и для копирования объекта:
пусть obj = {a: 1, b: 2, c: 3}; пусть objCopy = { ...obj }; // распространяем объект на список параметров // затем возвращаем результат в новый объект // объекты имеют одинаковое содержимое? alert(JSON.stringify(obj) === JSON.stringify(objCopy)); // истинный // объекты равны? Оповещение (obj === objCopy); // ложь (не та же ссылка) // изменение нашего исходного объекта не приводит к изменению копии: объект.д = 4; оповещение(JSON.stringify(obj)); // {"a":1, "b":2, "c":3, "d":4} оповещение(JSON.stringify(objCopy)); // {"a":1, "b":2, "c":3}
Этот способ копирования объекта намного короче, чем let objCopy = Object.assign({}, obj)
или для массива let arrCopy = Object.assign([], arr)
поэтому мы предпочитаем использовать его всякий раз, когда можем.
Когда мы видим "..."
в коде, это либо остальные параметры, либо синтаксис распространения.
Есть простой способ отличить их:
Когда ...
находится в конце параметров функции, это «остальные параметры» и собирает остальную часть списка аргументов в массив.
Когда ...
встречается при вызове функции или чем-то подобном, это называется «синтаксисом расширения» и расширяет массив в список.
Используйте шаблоны:
Остальные параметры используются для создания функций, принимающих любое количество аргументов.
Синтаксис расширения используется для передачи массива функциям, которым обычно требуется список из множества аргументов.
Вместе они помогают легко перемещаться между списком и массивом параметров.
Все аргументы вызова функции также доступны в arguments
«старого стиля»: итерируемом объекте, подобном массиву.