Во вчерашней статье про сортировку слиянием мы использовали такой код в конце функции:
return [ ...arr, ...left, ...right ]
Судя по квадратным скобкам, мы возвращаем массив, но в нём творится что-то странное: перед каждым элементом стоят точки, да ещё каждый из этих элементов тоже массив. Рассказываем, как такое возможно и что здесь делают три точки.
Кратко главное
- Конкретно в этом случае три точки — это оператор расширения.
- Он «разбивает» объект на набор его элементов и отдаёт их по порядку.
- Можно представить, что если объект — это набор чего-то в обёртке, то оператор расширения надрывает эту обёртку, и из объекта всё высыпается.
- Оператор можно использовать и наоборот: собрать массу каких-то значений в один объект.
Простой пример. У нас есть массив problems = ['сессия', 'отчет', 'здоровье']
. Если просто сказать console.log(problems)
, то выведется целый объект (как бы в обёртке), и работать с ним дальше нужно именно как с объектом:
❌console.log(problems)
> (3) ['сессия', 'отчет', 'здоровье']
Если нам не нужен объект, а нужно просто посмотреть на значения подряд, то делаем так:
✅console.log(...problems)
> сессия отчет здоровье
А теперь наоборот. Допустим нам нужна функция, которая разом решает все проблемы, но мы не знаем, сколько этих проблем будет. Тогда этот оператор примет любое количество значений и соберёт их в один объект:
function solve(...allProblems){ }
Теперь эта функция примет любое число значений и уложит их в общий массив allProblems
.
Оператор расширения
Три точки в JavaScript имеют два значения: они обозначают остаточные параметры или работают как оператор расширения. В нашем примере с функцией это как раз оператор расширения, поэтому начнём с него.
Смысл оператора расширения — преобразовать объект в перечислимый список. При этом сам объект тоже должен содержать значения, которые можно посчитать и к которым можно обратиться по отдельности. Например, это может быть массив — можно перечислить по очереди всё его содержимое. Или это может быть строка — мы можем перебрать в ней каждый символ.
На практике это может работать так. Допустим, у нас есть массив с числами, и нам нужно найти наибольшее из них:
var a = [2,7,3, 5, 4];
Но если мы используем обычную команду поиска максимума Math.max(a)
, то ничего не выйдет: ей нужно несколько чисел, а не один большой объект:
Чтобы обойти это ограничение, используют оператор расширения — он сделает из массива отдельные значения, которые будут использоваться как параметры. В этом случае вызов команды будет выглядеть так: Math.max(...a)
Получается, что мы расширили наш объект и виртуально превратили его из единого целого в несколько отдельных значений.
Вернёмся к нашему примеру:
return [ ...arr, ...left, ...right ]
Теперь, зная как работает оператор расширения, мы понимаем, что функция возвращает массив (потому что квадратные скобки), который собирается из трёх частей:
- Сначала в массив войдут все элементы массива
arr
(а не сам массив). - Потом все элементы массива
left
. - И напоследок — все элементы массива
right
.
Самое интересное, что если какой-то элемент окажется пустым, то от него не войдёт ничего. Вот как это работает на практике: допустим, у нас есть три массива:
- в
arr
будет лежать [2,7,0]; - массив
left
будет пустым; - а в
right
— [1,2].
Тогда команда return [ ...arr, ...left, ...right ]
вернёт такой массив: [2,7,0,1,2].
Остаточные параметры
Второй способ применения трёх точек в JavaScript — использовать их как остаточный параметр. По сравнению с оператором расширения остаточный параметр работает ровно наоборот: он собирает все значения в один массив. Чтобы было проще понять, покажем на примере.
Допустим, у нас есть функция, которая складывает переданные ей параметры и возвращает сумму:
function s(a, b) {
return a + b;
}
Но эта функция может работать только с двумя параметрами: если ей передать три, то она отбросит лишние и не будет их учитывать при расчётах:
А вот с помощью остаточных параметров мы можем собрать все аргументы вместе в один массив, а потом просто перебрать его и найти сумму всех значений:
function s(...args) {
// тут будет храниться промежуточная сумма
let sum = 0;
// перебираем все элементы массива args и добавляем их к сумме
for (let arg of args) sum += arg;
// возвращаем сумму
return sum;
}
console.log(s(2,4,6,8,10))
Этот приём можно использовать везде, где вы точно не знаете, сколько значений нужно будет обработать, но при этом нужно заранее предусмотреть все возможные ситуации.