JavaScript ES6: оператор расширения

https://codeburst.io/javascript-es6-the-spread-syntax-f5c35525f754
  • Перевод
JavaScript постоянно развивается, в нём появляются различные новшества и улучшения. Одно из таких новшеств, появившееся в ES6 — оператор расширения. Он выглядит как троеточие (...). Этот оператор позволяет разделять итерируемые объекты там, где ожидается либо полное отсутствие, либо наличие одного или нескольких аргументов. Сухие определения обычно бывает непросто понять без практических примеров. Поэтому рассмотрим несколько вариантов использования оператора расширения, которые помогут вникнуть в его сущность.

image

Пример №1: вставка массивов в другие массивы


Взгляните на этот код. Тут оператор расширения не используется:

var mid = [3, 4];
var arr = [1, 2, mid, 5, 6];

console.log(arr);

Выше мы создали массив mid. Затем создан второй массив, arr, который содержит массив mid. В конце программы массив arr выводится в консоль. Как вы думаете, каким станет этот массив после добавления в него массива mid? Взглянем на то, что выведет программа:

[1, 2, [3, 4], 5, 6]

Вы думали, что так и будет?

Вставляя массив mid в массив arr, мы получили в итоге один массив, вложенный в другой. Если это именно то, что было нужно, то придираться тут не к чему. Однако, что если целью написания вышеприведённого кода было получение массива чисел от 1 до 6? Для того чтобы достичь этой цели, можно использовать оператор расширения. Вспомните о том, что этот оператор позволяет разделять массивы на отдельные элементы.

Рассмотрим следующий пример. Всё тут, за исключением одного места, точно такое же, как и в предыдущем фрагменте кода. Разница заключается в том, что теперь для того, чтобы вставить содержимое массива mid в массив arr, применяется оператор расширения:

var mid = [3, 4];
var arr = [1, 2, ...mid, 5, 6];

console.log(arr);

Если этот код выполнить, то в результате будет выведено следующее:

[1, 2, 3, 4, 5, 6]

Вспомните описание оператора расширения, приведённое в самом начале материала. Только что вы увидели его в действии. Как можно заметить, когда мы создаём массив arr и используем оператор расширения, применяя его к массиву mid, то, вместо вставки в один массив другого массива, как объекта, этот другой массив «разбивается на части». Разделение вставляемого массива в нашем случае означает, что все элементы этого массива, поодиночке, будут добавлены в массив arr. В результате, вместо конструкции из вложенных массивов, получился один массив, содержащий числа от 1 до 6.

Пример №2: математические вычисления


В JavaScript есть встроенный объект Math, который позволяет выполнять математические вычисления. В данном примере нас интересует метод Math.max(). Если вы с этим методом не знакомы, сообщаем, что он возвращает самое большое из переданных ему чисел, причём допустимо использовать его как без аргументов, так и с одним или несколькими аргументами. Вот несколько примеров:

Math.max();
// -Infinity
Math.max(1, 2, 3);
// 3
Math.max(100, 3, 4);
// 100

Как видите, если требуется найти максимальное значение нескольких чисел, Math.max() нужно несколько параметров. К сожалению, если надо найти максимальный элемент числового массива, сам массив методу Math.max() передать нельзя. До появления в JS оператора расширения самым простым способом поиска максимального элемента в массиве с помощью Math.max() было использование метода apply():

var arr = [2, 4, 8, 6, 0];

function max(arr) {
  return Math.max.apply(null, arr);
}

console.log(max(arr));

Выше представлена работающая конструкция, но выглядит всё это не очень-то приятно.

А вот как то же самое делается с помощью оператора расширения:

var arr = [2, 4, 8, 6, 0];
var max = Math.max(...arr);

console.log(max);

Вместо того, чтобы создавать функцию и использовать метод apply() для возвращения результата работы метода Math.max(), тут нужно лишь две строки кода. Оператор расширения «вытаскивает» из массива все его элементы и они поступают на вход метода Math.max().

Пример №3: копирование массивов


В JS нельзя скопировать массив, просто приравняв новую переменную той, которая уже содержит существующий массив. Рассмотрим пример:

var arr = ['a', 'b', 'c'];
var arr2 = arr;

console.log(arr2);

Если его выполнить, можно увидеть следующее:

['a', 'b', 'c']

На первый взгляд всё работает как надо, может показаться, что мы скопировали значения массива из переменной arr в переменную arr2. Однако на самом деле произошло совсем другое. В JavaScript, в операциях присваивания объектов переменным (а массивы — это тоже объекты), оперируют ссылками на них, а не их значениями. Это означает, что в arr2 была записана та же ссылка, которая хранилась в arr. Другими словами, всё, что мы сделаем после этого с arr2, повлияет и на arr (и наоборот). Взгляните на это:

var arr = ['a', 'b', 'c'];
var arr2 = arr;

arr2.push('d');

console.log(arr);

Тут мы поместили новый элемент, строку d, в конец массива arr2. Однако, выведя в консоль arr, можно увидеть, что массив, на который ссылается эта переменная, также изменился:

['a', 'b', 'c', 'd']

Надо заметить, что ничего страшного тут не происходит. Перед нами — стандартное поведение JS. А для того, чтобы действительно создать копию массива, можно воспользоваться оператором расширения. Вот пример использования этого оператора для копирования массивов. Код выглядит практически так же, как и в вышеприведённом примере. Однако здесь используется оператор расширения, применённый к исходному массиву, а вся эта конструкция помещена в квадратные скобки:

var arr = ['a', 'b', 'c'];
var arr2 = [...arr];

console.log(arr2);

Выполнив этот код, можно увидеть, что выводит он то, чего мы от него и ожидаем:

['a', 'b', 'c']

В этом примере массив arr «разворачивается», в нашем распоряжении оказываются его отдельные элементы, которые попадают в новый массив, ссылка на который записывается в arr2. Теперь можно делать с arr2 что угодно и это не повлияет на arr:

var arr = ['a', 'b', 'c'];
var arr2 = [...arr];

arr2.push('d');

console.log(arr);

Опять же, причина, по которой это всё работает, заключается в том, что оператор расширения «вытаскивает» значения элементов массива arr и они попадают в новый массив arr2. Таким образом, мы записываем в arr2 ссылку на новый массив, содержащий элементы из массива arr, а не ссылку на тот массив, на который ссылается переменная arr. Это и отличает данный пример от предыдущего.

Дополнительный пример: преобразование строки в массив


Напоследок — вот один интересный пример использования оператора расширения для преобразования строк в массивы. Здесь достаточно применить этот оператор к нужной строке, поместив всю эту конструкцию в пару квадратных скобок:

var str = "hello";
var chars = [...str];

console.log(chars);

Итоги


Сегодня мы рассмотрели особенности работы с оператором расширения. Это — одна из новых возможностей JavaScript ES6, полезная мелочь, которая вполне способна улучшить читаемость кода и немного сократить его размер.

Уважаемые читатели! Пользуетесь ли вы оператором расширения в JavaScript?

RUVDS.com 540,11
RUVDS – хостинг VDS/VPS серверов
Поделиться публикацией
Комментарии 18
  • +11
    JavaScript ES6: оператор расширения

    Не надо так обзывать спред оператор. Вас никто не поймёт.
    • +3

      "оператор размазывания"

      • 0
        «оператор распаковки»? По крайней мере в python подобная операция именно так называется.
      • 0
        интересный пример «копирование массивов»
        не знал что так можно было)
      • 0

        arr2.push['d']; со скобками опечатались

      • +1
        Один из самых классных применений пропустили.
        Удаление дубликатов из массива:
        var myArray = ['a', 1, 'a', 2, '1'];
        let unique = [...new Set(myArray)];
        // equal to
        var items = [4,5,4,6,3,4,5,2,23,1,4,4,4]
        var uniqueItems = Array.from(new Set(items))
        


        Что существенно лучше вариантов с фильтром:
        var myArray = ['a', 1, 'a', 2, '1'];
        var unique = myArray.filter(function(v, i, a) {
          return  a.indexOf(v) === i;
        } 
        

        И не такое громоздкое как с помощью .reduce():
        let arr = [1, 2, 1, 2, 3, 5, 4, 5, 3, 4, 4, 4, 4];
        let result = arr.sort().reduce((init, current) => {
            if (init.length === 0 || init[init.length - 1] !== current) {
                init.push(current);
            }
            return init;
        }, []);
        
        • +3
          Только, к сожалению, у spread производительность низкая. Даже, казалось бы, громоздкий sort().reduce и то оказывается быстрее.
          • –2

            Вы бы еще на массиве из одного элемента производительность измеряли...


            На больших объемах spread оказывается самым быстрым.

            • +3
              И часто в типичных проектах приходится фильтровать массив из 10к обьектов? Намного чаще приходится работать с массивами в 20-100 элементов, иногда даже внутри циклов (например, при парсинге того же самого массива в 10к обьектов)
              • –1

                Если в массиве всего 10 элементов — то способ не важен, потому что узким местом этот код точно не будет являться, если только он не выполняется внутри цикла.


                Что же до вашего варианта "устраняем дубликаты в массиве из 10 элементов в цикле" — то такой странный код лучше просто не писать.


                Кстати, уже на 100 элементах spread обгоняет sort().reduce...

          • +3
            Конечно, мы же код пишем оценивая его громоздкость, О большое для дураков.
            • 0

              Асимптотика у [...new Set(myArray)] тоже в порядке — линейная, в отличии от квадратичной у фильтра и N log N у громоздкого sort().reduce()

              • –2
                вы не поверите, но во множестве случаев, то как код выглядит и читается важней производительности
            • 0

              Использование с генераторами:


              function* fibonacci ( n )
              {
                  const infinite = !n && n !== 0;
                  var current = 1,
                      next    = 1;
                  yield current;
                  n && n--;
                  while ( infinite || n-- ) {
                      [current, next] = [next, current + next];
                      yield current;
                  }
                  return current;
              }
              
              var [...arr] = fibonacci(10);
              // [ 1, 1, 2, 3, 5, 8, 13, 21, 34, 55 ]
              
              • +1

                Куда-то не туда он развивается

                • –2
                  Привет из Perl'а, где «разворачивание» массивов(и даже хэшей) по ссылке — это конструкция языка уже больше 20 лет

                  Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                  Самое читаемое