Использование оператора Spread в полной мере

Сначала мы рассмотрим основы оператора спреда, а затем расскажем о некоторых малоизвестных его применениях.

Оператор Spread позволяет расширять массивы и объекты.

Во-первых, вы могли заметить, что операторы Spread и Rest выглядят совершенно одинаково. Оба они используют один и тот же синтаксис с тремя точками (), но на самом деле одно можно рассматривать как противоположность другому. То есть они совершают противоположные вещи. Мы рассмотрим использование оператора распространения для массивов и объектов отдельно.

Распространение массивов

Как указано выше, оператор расширения расширяет массив на его элементы, где ожидается одно или несколько значений. Следствием этого является то, что массив будет сглажен. Давайте посмотрим на пример.

Допустим, вы хотите соединить или объединить два массива, поэтому вы идете вперед и, исходя из лучших побуждений, придумываете следующее:

const arr = [4, 5, 6];
const resultArr = [1, 2, 3, arr];

Если вы console.log resultArr, вы увидите, что вывод: [1, 2, 3, [4, 5, 6]]. Ой! Не совсем то, что мы хотели. Вот где появляется оператор спреда. Если вместо этого мы введем const resultArr = [1, 2, 3, …arr], вы увидите, что будет напечатано следующее: [1, 2, 3, 4, 5, 6]. Хороший. Надеюсь, из этого примера теперь понятно, что имеется в виду, когда я говорю, что массив расширен.

Таким образом, объединение массивов является одним из наиболее распространенных применений оператора расширения:

const arr1 = [1, 2, 3];
const arr2 = [4, 5, 6];
const resultArr = [...arr1, ...arr2];

Имейте в виду, что распространение массива или объекта создаст его новую копию с новой ссылкой в ​​памяти, так что это отличная новость для неизменяемости.

Перед оператором распространения вам нужно было полагаться на некоторые неизменяемые методы массива, чтобы копировать или объединять массивы. Когда я говорю неизменяемый, я имею в виду методы, которые не мутируют входящий массив, то есть они работают с новой и свежей копией входящего массива и вместо этого возвращают ее.

const arr1 = [1, 2, 3];
const arr2 = [4, 5, 6];
const resultArr = arr1.concat(arr2); // Concatenates two arrays
const copyArr = arr1.concat([]); // Creates a new copy of arr
const copyArr = arr1.slice(0); // Also creates a new copy of arr

Распространение объектов

Оператор распространения работает с объектами так же, как и с массивами. Это также позволяет нам копировать объекты.

Копирование объектов

const user1: User = {
  id: 'some_id',
  firstName: 'Foo',
  lastName: 'Bar',
  email: '[email protected]',
};

const user2: User = { ...user1 };

Как видите, синтаксис очень похож на распространение массивов.

Применение частичных обновлений или модификаций

Вы можете использовать оператор распространения не только для копирования объекта, но и для копирования и одновременного применения частичных обновлений или модификаций его свойств.

const user1: User = {
  id: 'some_id',
  firstName: 'Foo',
  lastName: 'Bar',
  email: '[email protected]',
};

const user2: User = {
  ...user1,
  firstName: 'Another Name',
  lastName: 'Another Last Name',
};

const user3: User = { ...user2 };

Копирование и обновление глубоких и вложенных объектов

Имейте в виду, что не имеет значения, насколько глубокой или вложенной может быть форма объекта, с которым вы работаете. Вы можете продолжать применять оператор спреда на каждом уровне, копируя каждый уровень по мере необходимости:

const nestedObject: NestedObject = {
  a: {
    b: {
      c: {
        d: {
          e: {
            someProp: 'test',
            anotherProp: true,
            yetAnotherNestedProp: { itEndsHere: 7 },
          },
        },
      },
    },
  },
};

const anotherNestedObject: NestedObject = {
  ...nestedObject,
  a: {
    ...nestedObject.a,
    b: {
      ...nestedObject.a.b,
      c: {
        ...nestedObject.a.b.c,
        d: {
          ...nestedObject.a.b.c.d,
          e: {
            ...nestedObject.a.b.c.d.e,
            someProp: 'change',
            anotherProp: false,
          },
        },
      },
    },
  },
};

Возможно, это немного преувеличено, но суть вы поняли. Здесь мы скопировали nestedObject до e. Затем мы обновили значения someProp и anotherProp. Поскольку мы скопировали e с ...nestedObject.a.b.c.d.e, реквизит yetAnotherNestedProp сохранит то же значение, что и в исходном nestedObject. Короче говоря, использование оператора распространения скопирует объект и все его свойства, поэтому нам просто нужно обновить или изменить те, которые нас интересуют, на каждом уровне объекта.

Объединение объектов

Обратите внимание, что при использовании оператора распространения для объединения 2 или более объектов, если они имеют общие свойства, в качестве источника истины будет использоваться один из последнего объекта распространения:

const user1 = {
  id: 'some_id',
  firstName: 'Foo',
  lastName: 'Bar',
  email: '[email protected]',
};

const user2 = {
  ...user1,
  firstName: 'Another Name',
};

const finalUser = { ...user1, ...user2 };

Если вы console.log finalUser, вы увидите, что firstName содержит значение Другое имя.

Распространенные и не очень распространенные способы использования оператора спреда

Мы вернемся к наиболее распространенным и известным способам использования оператора спреда, а также рассмотрим некоторые другие интересные трюки, которые мы можем с ним сделать:

  • Копирование объекта: если вы работаете с React, возможно, вы использовали это некоторое время, не зная об этом, потому что это связано с неизменяемостью, одним из основных принципов Redux. . В двух словах, мы можем использовать оператор распространения, чтобы убедиться, что мы не мутируем объект, а всегда работаем с новой и свежей его копией. Почему это важно? Изображение говорит больше, чем 1000 слов:

Здесь мы не используем оператор распространения, поэтому мы фактически мутируем объект user. Что это значит? Это userCopy буквально совпадает с user . Он указывает на ту же ссылку, на то же место в памяти, поэтому изменение свойства userCopy, как я сделал в предыдущем фрагменте, также будет отражено в user. После этой строки и user.firstName, и userCopy.firstName будут содержать значение ‘независимо’. Это нежелательно и может быстро привести к ошибкам и проблемам, которые будет очень сложно отладить и отследить. Как указано выше, вы также можете использовать оператор распространения для копирования массивов.

  • Применить частичные обновления или модификации. Используя оператор распространения, мы можем безопасно скопировать объект user и обновить любые реквизиты, какие захотим, и быть уверенными, что эти изменения никак не повлияют на исходный объект user:

Имейте в виду, что если вы хотите обновить вложенные свойства, мы должны обязательно скопировать их на каждом уровне и обновить интересующие нас свойства на каждом уровне:

  • Объединить объекты. Мы можем использовать оператор распространения для объединения двух и более объектов:

  • Объединить массивы. Мы можем использовать оператор распространения для объединения двух и более массивов:

  • Создайте массив уникальных значений, также называемый набором:[…new Set(arr)]. Я должен сказать, что мне очень нравится этот!😍
  • Расширение массива или кортежа в вызовах функций. Если у нас есть массив или кортеж, мы можем использовать оператор расширения, чтобы расширить его элементы в вызове функции, чтобы каждый элемент был сопоставлен с параметром функции:

Это работает, потому что, если вы посмотрите на сигнатуру Math.min или Math.max, вы увидите, что они принимают остальные параметры типа number[]. Я планирую написать статью, посвященную остаточным параметрам и, как следствие, оставшемуся оператору. В начале этой статьи я упомянул о том, что операторы спреда и остатка выглядят абсолютно одинаково, однако выполняют несколько противоположные действия. Мы можем использовать этот пример, чтобы лучше понять эту связь и отношения. Метод ожидает ...number[] и у нас есть массив. Итак, мы знаем, что оператор расширения расширяет массив на его элементы.

Мы можем сделать что-то подобное с кортежами, не работая с остальными параметрами:

⚠️ ️Обратите внимание, что Const Assertionas const) необходим для работы приведенного выше фрагмента. Почему? Я написал статью Const Assertions and Type Narrowing & Widening, в которой я подробно объясняю это.

Утверждения константы, сужение и расширение типов в TypeScript: https://medium.com/@taitasciore/const-assertions-and-type-narrowing-widening-in-typescript-72005b201f28

Без него компилятор TypeScript будет кричать на нас Аргумент распространения должен либо иметь тип кортежа, либо передаваться в параметр rest. Что это значит?

По сути, someMethod предполагает фиксированное количество аргументов: 3 в данном случае и типа number . Мы знаем, что arr — это массив чисел. Массивы, в отличие от кортежей, не имеют фиксированной длины или размера, поэтому их можно изменять. Поскольку они могут иметь произвольное количество элементов, а someMethod ожидает только 3 аргумента, TypeScript не позволит нам вызвать функцию таким образом. Однако, превращая arr в кортеж с помощью Const Assertion, TypeScript понимает, что это безопасно для нас, поскольку он знает, что arr относится к типу [number, number, number]. Ну точнее типа readonly [1, 2, 3] . В конце концов, это будет то же самое, что и вызов функции типа someMethod(1, 2, 3).