04.02.2017 4693 2

Методы массивов в JavaScript: С мутацией или без

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

Ниже я попытался изложить, как можно выполнить три эти задачи, используя оба вида методов. В завершении статьи я покажу как обходить массив и изменять его элементы используя немутирующий array.map().

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

ЗАМЕТКА: Ниже я создаю массивы используя const, когда используются немутирующие методы, а для мутирующих использую let. Хоть мы и можем видоизменять массив, созданный при помощи const без выбрасывания ошибки, я использую const как сигнал для других разработчиков, что созданный массив не будет изменен.

ВНИМАНИЕ: Пока будете читать эту статью, обращайте особое внимание разнице между:

  1. array.splice(), который мутирует изначальный массив, и
  2. array.slice(), который этого не делает.

I. Добавление: С мутацией

Мутирующими методами для добавления элементов в массив являются array.push() и array.ushift().

// в случае, если массив мутируется
// используем  'let' вместо 'const'
let mutatingAdd = ['a', 'b', 'c', 'd', 'e']; 

mutatingAdd.push('f'); // ['a', 'b', 'c', 'd', 'e', 'f']
mutatingAdd.unshift('z'); // ['z', 'a', 'b', 'c', 'd', 'e', 'f']

Это код иллюстрирует, что:

  • array.push() добавляет элемент в конец массива
  • array.unshift() добавляет элемент в начало массива

II. Добавление: Без мутации

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

Во-первых, есть array.concat().

// так как массив не мутируется
// используем const
const arr1 = ['a', 'b', 'c', 'd', 'e'];

const arr2 = arr1.concat('f'); // ['a', 'b', 'c', 'd', 'e', 'f']
console.log(arr1); // ['a', 'b', 'c', 'd', 'e']

Второй способ для добавления элементов без мутации — это использование оператора расширения. Оператор расширения записывается в виде трех точек (...) предшествующих массиву.

const arr1 = ['a', 'b', 'c', 'd', 'e'];

const arr2 = [...arr1, 'f']; // ['a', 'b', 'c', 'd', 'e', 'f']
const arr3 = ['z', ...arr1]; // ['z', 'a', 'b', 'c', 'd', 'e']

В примере выше, оператор расширения будет копировать оригинальный массив, доставать все его элементы и размещать их в новом контексте.

Сначала мы берем копии элементов из arr1, помещаем их в новый массив и в конец добавляем 'f'.

Далее происходит почти то же самое, но новый элемент 'z' добавляется перед другими элементами.

III. Удаление: С мутацией

Методы для удаления элементов с мутацией — это array.pop() и array.shift().

let mutatingRemove = ['a', 'b', 'c', 'd', 'e'];
mutatingRemove.pop(); // ['a', 'b', 'c', 'd']
mutatingRemove.shift(); // ['b', 'c', 'd']

Этот код иллюстрирует, что:

  • array.pop() удаляет элемент с конца массива.
  • array.shift() удаляет элемент с начала массива.

array.pop() и array.shift() возвращают элемент, который был удален. Это означает, что вы можете "поймать" удаленный элемент и поместить в переменную.

let mutatingRemove = ['a', 'b', 'c', 'd', 'e'];

const returnedValue1 = mutatingRemove.pop();  
console.log(mutatingRemove); // ['a', 'b', 'c', 'd']  
console.log(returnedValue1); // 'e'

const returnedValue2 = mutatingRemove.shift();  
console.log(mutatingRemove); // ['b', 'c', 'd']  
console.log(returnedValue2); // 'a'

Также есть array.splice() для удаления элементов массива.

let mutatingRemove = ['a', 'b', 'c', 'd', 'e'];  
mutatingRemove.splice(0, 2); // ['c', 'd', 'e']

mutatingRemove.splice(0, 2) на примере выше принимает два параметра (он может принимать больше двух, подробнее ниже).

  1. Первый параметр — это начальная позиция, с которой будут осчитываться элементы.
  2. Второй параметр задает количество элементов, которые нужно удалить.

В примере выше, два элемента удаляются из массива mutatingRemove (второй аргумент), начиная с 0 индекса (первый аргумент).

Как и array.pop() и array.shift(), array.splice() возвращает удаленные элементы.

let mutatingRemove = ['a', 'b', 'c', 'd', 'e'];
let returnedItems = mutatingRemove.splice(0, 2);
console.log(mutatingRemove); // ['c', 'd', 'e']
console.log(returnedItems) // ['a', 'b']

IV. Удаление: Без мутации

Метод JavaScript-а array.filter() создает новый массив из первоначального массива, но новый содержит только те элементы, которые соотвествуют заданному критерию.

const arr1 = ['a', 'b', 'c', 'd', 'e'];

const arr2 = arr1.filter(a => a !== 'e'); // ['a', 'b', 'd', 'f']  
// ИЛИ
const arr2 = arr1.filter(a => {  
  return a !== 'e';
}); // ['a', 'b', 'd', 'f']

В этом примере, критерием для отсеивания является неравенство 'e', поэтому новый массив (arr2) почти такой же, как и оригинальный, но содержащий только те элементы, которые не равны 'e'.


Некоторые особенности стрелочных функций:

Для однострочных стрелочных функций ключевое слово return подразумевается по умолчанию, так что вам не нужно писать его.

Однако для многострочных стрелочных функций нужно явно указывать возвращаемое значение.


Другой способ удалить элементы из массива без мутации — это использование array.slice(). (Не путать с array.splice())

array.slice() принимает два аргумента.

  1. Первый аргумент указывает откуда должна начинаться копия.

  2. Второй аргумент задает последний индекс не включительно.

const arr1 = ['a', 'b', 'c', 'd', 'e'];

const arr2 = arr1.slice(1, 5) // ['b', 'c', 'd', 'e']
const arr3 = arr1.slice(2) // ['c', 'd', 'e']

На строке с кодом const arr2 = arr1.slice(1, 5)arr2 создается путем копирования arr1 начиная с индеса 1 и заканчивая предыдущим индексом для 5 (то есть 4).

На следующей строке const arr3 = arr1.slice(2) показан полезный трюк. Если второй параметр метода array.slice() не задан, то метод берет копию с начального индекса до конца массива.

V. Замена: С мутацией

Если вы знаете индекс элемента, который вы хотите заменить, вы можете использовать array.splice().

Для этого, нужно использовать хотя бы 3 аргумента:

  1. Первый аргумент задает начальный индекс.

  2. Второй задает количество элементов для замены.

  3. Третий и все последующие аргументы — это элементы, которые будут подставлены в массив.

let mutatingReplace = ['a', 'b', 'c', 'd', 'e'];

mutatingReplace.splice(2, 1, 30); // ['a', 'b', 30, 'd', 'e'] // ИЛИ
mutatingReplace.splice(2, 1, 30, 31); // ['a', 'b', 30, 31, 'd', 'e']

mutatingReplace.splice(2, 1, 30) заменяет 'c' на 30.

mutatingReplace.splice(2, 1, 30, 31) удаляет 'c' и добавляет 30 и 31.

VI. Замена: Без мутации

Мы можем использовать array.map(), чтобы создать новый массив, но мы также можем проверить каждый элемент на соответсвие условию и заменить их.

const arr1 = ['a', 'b', 'c', 'd', 'e']  
const arr2 = arr1.map(item => {  
  if(item === 'c') {
    item = 'CAT';
  }
  return item;
}); // ['a', 'b', 'CAT', 'd', 'e']

Приведенный код создает новый массив основанный на arr1, но заменяет все 'c' на котов (CAT).

Преобразование данных при помощи array.map()

array.map() является полезным методом, который может быть использован для преобразования данных, не ставя под угрозу целостность изначальных данных.

const origArr = ['a', 'b', 'c', 'd', 'e'];  
const transformedArr = origArr.map(n => n + 'Hi!'); // ['aHi!', 'bHi!', 'cHi!', 'dHi!', 'eHi!']  
// ИЛИ
const transformedArr = origArr.map(n => {  
  return n + 'Hi';
})// ['aHi!', 'bHi!', 'cHi!', 'dHi!', 'eHi!']
console.log(origArr); // ['a', 'b', 'c', 'd', 'e']; // оригинальный массив нетронут

Если вам понравилась эта статья, поделитесь ею с другими.

Статью перевел aziev. Оригинал на Lorenstewart.me доступен по ссылке.