Вот почему создание массива через map не работает в JS

Вот почему создание массива через map не работает в JS

Кейс

Для демонстрации предположим, что вам нужно создать массив чисел от 0 до 99. Как вы можете это сделать? Вот один из вариантов:

const arr = [];
for (let i = 0; i < 100; i++) {
  arr[i] = i;
}

Если вы схожи со мной, то при виде традиционного цикла for в JavaScript вас немного передергивает. Фактически, я не писал традиционный цикл несколько лет благодаря функциям более высокого порядка, таким как forEach, map, filter и reduce.

Возможно, вы еще не пробовали функциональное программирование Kool-aid, и вы думаете, что вышеупомянутое решение кажется совершенно прекрасным. Технически это так, но после того, как вы почувствовали привкус магии функций высшего порядка, вы, наверное, думаете, «Должно быть решение лучше».

Моя первая реакция на эту проблему была: « Я знаю! Я создам пустой массив длиной 100 и сопоставляю индексы каждому элементу! «JavaScript позволяет создать пустой массив длины n с помощью конструктора Array, например:

const arr = Array(100);

Идеально, правда? У меня есть массив длиной 100, поэтому теперь мне просто нужно сопоставить индекс с каждым элементом.

const arr = Array(100).map((_, i) => i);
console.log(arr[0] === undefined);  // true

Что такое х*рня !? Первый элемент массива должен быть 0 , но на самом деле он undefined!

Объяснение

Мне нужно сделать важное техническое замечание, чтобы объяснить, почему это произошло. Внутренне, массивы JavaScript - это объекты, а номера - это ключи. Например:

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

по существу эквивалентно этому объекту:

{
  0: 'a',
  1: 'b',
  2: 'c',
  length: 3
}

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

Когда вы создаете новый массив через конструктор Array, он создает новый объект массива с свойством length, установленным в значение, которое вы передали, но с другой стороны - объект является вакуумом. В объектном представлении массива нет никаких ключей индексов.

{
  //no index keys!
  length: 100
}

Вы получаете undefined при попытке получить доступ к значению массива c индексом 0, но это не значит, что значение undefined хранится в индексе 0, так как по умолчанию в JavaScript должен возвращать undefined, если вы пытаетесь получить доступ к значению объекта по ключу, которого не существует.

Так получилось, что функции более высокого порядка map, reduce, filter, and forEach итерируют по ключам индекса объекта массива от 0 до длины, но обратный вызов выполняется, только если ключ существует в объекте. Это объясняет, почему наш колбек никогда не вызывается, и ничего не происходит, когда мы вызываем функцию map в массиве - в массиве нет ключей индекса!

Решение

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

const arr = [...Array(100)].map((_, i) => i);

Расширение массива в пустой массив приводит к массиву, заполненному undefined для каждого индекса.

{
  0: undefined,
  1: undefined,
  2: undefined,
  ...
  99: undefined,
  length: 100
}

Это связано с тем, что spread оператор проще, чем функция map. Он просто перебирает массив (или любой итерируемый объект) от 0 до длины и создает новый индексный ключ в охватывающем массиве со значением, возвращаемым из массива расширения в текущем индексе. Поскольку JavaScript возвращает undefined из нашего расширяющего массива по каждому из его индексов (помните, что он делает это по умолчанию, потому что у него нет ключа индекса для этого значения), мы получаем новый массив, на самом деле заполненный индексными ключами, и поэтому можно применить map (и reduce, filter и forEach).

Вывод

Мы обнаружили некоторое представление массивов, которые внутренне представлены как объекты в Javascript, и узнали лучший интересный способ создания массивов произвольной длины, заполненных любыми значениями, которые вам нужны.

Как всегда, оставляйте свои комментарии, вопросы и отзывы ниже!

Удачного кодинга! 👍

Присоединяйтесь к нашим каналам FrontEndDev и Web Stack в Telegram, чтобы не пропустить самое интересное!

Оригинал статьи: Here’s Why Mapping a Constructed Array in JavaScript Doesn’t Work