Основы JavaScript

Краткое руководство по массивам JavaScript

Что это такое и как начать их эффективно использовать

Введение

Массивы — это структура, общая для всех языков программирования, поэтому знание того, что они из себя представляют, и четкое понимание того, что вы можете сделать с помощью массивов, поможет вам пройти долгий путь в вашем путешествии в качестве разработчика программного обеспечения. Примеры кода, которыми я делюсь в этом посте, будут на JavaScript, но концепции общие для всех языков. То, что вы узнаете здесь, может быть легко переведено на любой другой язык, с которым вы работаете.

В этом посте я расскажу, как выполнять операции создания, чтения, обновления и удаления с использованием массивов, некоторые общие функции, которые поставляются с прототипом массива, а также как их реализовать.

Что такое массив

Прежде чем мы перейдем к пикантным аспектам массивов, давайте кратко рассмотрим, что они из себя представляют. Массивы

  • являются фундаментальным типом данных в JavaScript
  • представляют собой упорядоченный набор значений, называемых элементами, которые хранятся в индексе и доступны через него.
  • являются нетипизированными, что означает, что элементы массива могут быть разных типов. Это позволяет нам создавать сложные массивы, такие как массив объектов или даже массив массивов (многомерные массивы).
  • может иметь элементы, которые являются константами или выражениями
  • иметь свойство length, которое сообщает вам количество элементов в массиве
  • наследовать свойства от Array.prototype, включая множество полезных функций, которые можно вызывать из массивов или array-like объектов

CRUD-операции с использованием массивов

Если вы не знакомы с термином CRUD, он означает создание, чтение, обновление и удаление. В этом разделе мы рассмотрим каждую из этих операций и рассмотрим различные способы их выполнения.

Создание массивов

Существует несколько способов создания массива, но наиболее распространенными способами являются использование

  • синтаксис литерала массива
  • конструктор массива, т.е. new Array()

Давайте рассмотрим каждый на примерах

Литерал массива

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

Конструктор массива

Другой способ создать массив — через конструктор Array.

const myArray = new Array();

Использование конструктора Array, как показано выше, аналогично созданию массива с синтаксисом литерала массива. то есть

// The following two lines behave exactly the same way i.e. both create an empty arrays
const myArray = new Array();
const myOtherArray = [];

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

  • Вы можете передать один аргумент числовой, который создает массив указанной длины. Эта опция в основном используется, когда вы знаете, сколько элементов вы будете размещать в массиве.
const myArray = new Array(5);

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

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

Как указывалось ранее, эти два способа являются наиболее распространенными способами создания массивов, которые вы увидите и будете использовать в 99% случаев. Есть еще несколько способов, но мы не будем углубляться в то, как они работают. Они есть

  • оператор спреда const someArray = […someOtherArray]
  • статический метод Array.of()
  • и статический метод Array.from()

Чтение, запись и обновление данных в массиве

Чтение, запись и обновление значений элементов в массиве так же просто, как и их создание. Вы можете выполнить все три операции, используя синтаксис квадратных скобок []. Давайте рассмотрим несколько примеров для каждой операции.

Здесь есть несколько вещей, на которые следует обратить внимание.

  • Индексы массива начинаются с 0
  • Мы размещаем значения в массиве, используя определенные индексы
  • Каждый раз, когда мы добавляем значение в массив, его свойство length обновляется.
  • Использование существующего индекса изменяет/обновляет значение этого индекса

Примечание. Массивы JavaScript не имеют ошибки "за пределами". Когда вы пытаетесь получить доступ к несуществующему индексу, вы не получаете сообщение об ошибке; вы просто получаете undefined.

Удаление из массива

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

Примечание. При удалении элемента из массива с помощью оператора delete массив становится sparse. Это означает, что свойство length массива остается таким же, каким оно было до удаления. Создание разреженного массива также означает, что элементы массива не сдвигаются, чтобы заполнить пробел.

Методы из класса массива

В предыдущем разделе мы рассмотрели некоторые основные операции, которые вы можете выполнять с массивами. Теперь мы углубимся в некоторые полезные методы, доступные в классе Array, а также посмотрим, как некоторые из них реализованы внутри. Эти методы варьируются от итерации по массиву до вставки, удаления и поиска элементов из массива. Список методов в этом посте ни в коем случае не является полным списком методов, которые находятся в вашем распоряжении, вместо этого это список некоторых наиболее часто используемых методов, которые я считаю.

Метод forEach()

Метод forEach() перебирает данный массив и вызывает указанную вами функцию для каждого элемента массива. Вот пример.

Примечание. Функция обратного вызова, которую вы передаете методу forEach(), может принимать 3 аргумента. Первый — это текущий элемент, второй — текущий индекс, а третий — сам массив. Чаще всего используются только первые два аргумента.

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

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

В приведенном выше примере я создал метод myForEach() внутри Array.prototype, и он принимает функцию обратного вызова. Использование этого нового метода точно такое же, как и обычного метода forEach(), и вы можете видеть, что внутри метода нет способа разорвать цикл.

Метод карты()

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

Примечание. Метод map() не изменяет существующий массив, а вместо этого возвращает совершенно новый массив, содержащий измененные значения. Кроме того, если исходный массив разреженный, ваша функция не будет вызываться для отсутствующих элементов, но возвращаемый массив будет разреженным так же, как и исходный массив.

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

Метод filter()

Как и метод map(), метод filter() также возвращает массив. Отличие массива, возвращаемого методом filter(), в том, что он является подмножеством исходного массива. Как и методы forEach() и map(), метод filter() также принимает функцию обратного вызова. Эта функция обратного вызова должна быть predicate, то есть должна возвращать значение true или false. Если условие равно true, то элемент, переданный в предикат, добавляется в массив подмножества, который будет возвращаемым значением метода filter().

Примечание. Метод filter() не изменяет исходный массив, а вместо этого возвращает новый массив, являющийся подмножеством исходного.

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

Метод уменьшения ()

Метод reduce() используется для объединения элементов массива с помощью функции, которую вы передаете, для получения одного значения. Метод принимает два параметра. Первая — это функция, которую вы хотите использовать для выполнения операции сокращения, а вторая (необязательно) — это начальное значение для использования.

Когда вы вызываете метод reduce() без второго параметра, то есть без начального значения для начала, он использует первый элемент массива в качестве начального значения, а второй элемент массива используется в качестве текущего значения.

Функция обратного вызова, которую вы передаете методу reduce(), немного отличается от тех, которые вы передаете в предыдущих рассмотренных методах. Для метода reduce() первый аргумент функции обратного вызова — это накопленное значение, а второй, третий и четвертый параметры — это элемент, индекс и массив соответственно. Чаще всего вы будете использовать только первые два значения, то есть накопленное значение и текущий обрабатываемый элемент.

Давайте рассмотрим, как это работает.

В первом вызове acc is 0 (поскольку мы передали 0 в качестве начального значения) и ele is 1 (поскольку это первый элемент массива), поэтому он вычисляет 0+1=1 и возвращает 1. Затем acc is 1 и ele is 2, так что он вычисляет 1+2=3. После этого acc is 3 и ele is 3 вычисляется 3+3=6. Наконец, acc is 6 и ele is 4, поэтому вычисляется 6+4=10. На данный момент больше нет значений для накопления, то есть в массиве больше нет элементов, поэтому возвращаемое значение для метода reduce() становится равным 10.

Давайте углубимся и посмотрим, как можно реализовать этот метод reduce().

Методы push() и pop()

Эти два метода позволяют использовать массивы, такие как стеки. Стеки следуют протоколу LIFO, что означает «последним пришел — первым ушел». Метод push() вставляет одно или несколько значений в конец массива и возвращает новую длину. В то время как метод pop() возвращает последнее значение массива, уменьшает длину и удаляет это последнее значение из массива.

Примечание. Оба этих метода изменяют массив на месте, а не создают его копию, как мы видели в map() и filter().

Давайте посмотрим, как каждый из них работает, а затем посмотрим, как каждый из них может быть реализован внутри.

Давайте теперь посмотрим, как это может быть реализовано внутри.

Вывод

Это ни в коем случае не исчерпывающий список методов, доступных в классе Array, но это список наиболее часто используемых методов. В этом посте мы рассмотрели некоторые основные операции CRUD, которые вы можете выполнять с массивами, а также немного углубились в некоторые из наиболее часто используемых методов класса Array и их реализацию. Имейте в виду, что массив — это структура данных, общая для всех языков программирования, поэтому то, что вы здесь узнали, можно применить и к другим языкам.

Что же, пока это все. До следующего раза, мир ✌️