Как стать автором
Обновить

Комментарии 29

Прост для полноты положу тут, как красиво создавать заполненные (неразрежённые) массивы без грязных хаков с апплаями:
const matrix = new Array(5).fill(
  new Array(5).fill()
)

По спеке вроде не надо нью у Array конструктора — поведение одинаковое — что без, что с.
Еще проще:


[...Array(5)].fill([...Array(5)])

Ага, согласен, ну тогда:


[...Array(5)].map(() => [...Array(5)])
Этот код аналогичен, тому, что в статье.
Array(...Array(5)).map(() => Array(...Array(5)));

Просто вы использовали способ создания массива, через литерал, а не функцию.
Но в целом, согласен — про литералы я сам в посте не упомянул и ваше дополнение вполне уместно.
куда проще
Array.from({length: 5}, () => Array.from({length: 5}))
Прост для полноты положу тут, как красиво создавать заполненные (неразрежённые) массивы без грязных хаков с апплаями:
const matrix = new Array(5).fill(
  new Array(5).fill()
)


Ваш код — некорректный:

Да, согласен, так получится заполнять только примитивными значениями массивы, зря я замахнулся на матрицы, пусть останется только одномерный случай и без нью :). Для матриц придётся маппить (что уже не так компактно, но всё ещё прозрачнее хаков с эпплаями, и fill красивее, имхо, чем вариант со спредом у комментатора выше :))
const matrix = Array(5).fill().map(
  () => Array(5).fill()
)
Спасибо всем, кто дополнительно привел свои варианты и тем кто нашел в части из них ошибки — ваши комментарии прекрасно дополняют материал.
Прост для полноты положу тут, как красиво создавать заполненные (неразрежённые) массивы без грязных хаков с апплаями:

const matrix = new Array(5).fill(
  new Array(5).fill()
)

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

На мой взгляд приведенный в посте способ с apply() самый простой для применения в средах исполнения до ECMAScript 6. Кроме того, описание работы конструктора Array и метода Function.prototype.apply() (касательно разбивки объекта подобного массиву), даёт лучшее понимание, того как работают остальные, в том числе, новые, методы со схожим функционалом в части работы с подобными объектами.
Мне кажется, в любом учебнике по JS написано, что
const arr = new Array(5)
создаёт массив без элементов, но с заданной длиной.
Это будет эквивалентно такому коду:
const arr = [];
arr.length = 5;
Да, согласен, написано. И, правильно, что вы заострили своё внимание на этом моменте — это лишнее напоминание о том, что для начала надо читать инструкцию по применению.
Однако, прочитав определение в учебнике, мы не всегда сразу можем увидеть из этого все возможные следствия, и, если, к тому же, мы не знаем в точности их внутреннюю составляющую, самостоятельно не обращаясь к спецификациям. И, этот пост создавался, в том числе, для того, чтобы подчеркнуть важность понимания внутренних механизмов языка на котором мы пишем.
НЛО прилетело и опубликовало эту надпись здесь
diomas выше писал:
так тоже будет ожидаемый результат:

const matrix = Array.from(Array(5), () => Array(5).fill())

Ваш код аналогичен.

Кроме того, касательно Array.from() — я уже писал в комментарии выше — этот метод работает только в ECMAScript 6 и до сих пор не имеет поддержки в некоторых браузерах (Array.from()), метод же Function.prototype.apply() единственный, который может использоваться везде или практически везде, но прежде всего целью статьи было, показать через внутренний принцип работы некоторых функций то как JavaScript работает с массивами и подобными им объектами.
Оператор spread, в частности, был, приведен в статье в качестве примера, в том числе, для того, чтобы показать, что не только Function.prototype.apply(), может проводить подобные фокусы. Использование других методов вроде from() и fill() конечно вполне себе законно и расширяет возможности, и где-то красоту, поэтому я, естественно согласен, что их можно и нужно использовать в соответствующих ситуациях.

На счет производительности думаю вы правы, но мне кажется этот вопрос выходит за рамки данной статьи. Но в качестве «справочно» сойдёт :)
НЛО прилетело и опубликовало эту надпись здесь
Array.from()
Настольные
Возможность Chrome Firefox (Gecko) Internet Explorer Opera Safari
Базовая поддержка 45 32 (32) Нет Нет 9.0

Мобильные
Возможность Android Chrome для Android Firefox Mobile (Gecko) IE Mobile Opera Mobile Safari Mobile
Базовая поддержка Нет Нет 32.0 (32) Нет Нет Нет

И, я не призываю писать всё время на ES5, повторюсь: цель статьи была не в том, чтобы обязательно вывести в ней какой ультимативный способ для создания массивов/матриц, а для разъяснения принципов работы некоторых внутренних инструментов JavaScript. И ничего против использования Array.from(), Array.prototype.fill(), оператора spread и прочего, я не имею.

НЛО прилетело и опубликовало эту надпись здесь
Спасибо, здесь вы правы, учту.
проблема этого кода
let arr = new Array(5).map(function() { return new Array(5); });

не в методе создания массива, а в методе `map`
первый же абзац говорит что `map` не вызывает callback если значение в массиве не было инициализированно. Но про этом если присвоить элементу массива `undefined` это считается проинициализированным.

var ar = new Array(5);ar[2] = void 0; ar.map((v,i) => console.log(v,i)) // undefined 2

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


new Array(5).fill(void 0).map(() => new Array(5));
Получится
/*
[…]
0: Array(5) [ <5 empty slots> ]
1: Array(5) [ <5 empty slots> ]
2: Array(5) [ <5 empty slots> ]
3: Array(5) [ <5 empty slots> ]
4: Array(5) [ <5 empty slots> ]
*/

а надо
/*
[…]
0: Array(5) [ undefined, undefined, undefined, … ]
1: Array(5) [ undefined, undefined, undefined, … ]
2: Array(5) [ undefined, undefined, undefined, … ]
3: Array(5) [ undefined, undefined, undefined, … ]
4: Array(5) [ undefined, undefined, undefined, … ]
*/

дополнив то, что вы написали, этого можно достичь так:
new Array(5).fill(void 0).map(() => new Array(5).fill(void 0));
или, еще проще:
new Array(5).fill().map(() => new Array(5).fill());

впрочем, чуть выше napa3um уже привел подобный пример.

Но всё же тащить void 0 в продакшн лучше не стоит :)

А зачем изначально инициализировать массив undefined через fill, и уже только потом использовать map что бы создать новые массивы?
Мне кажется код станет лучше если написать:


new Array(5).fill(new Array(5).fill());
Выше, уже ответили почему подобный код не корректен.
А в целом, рекомендую внимательней ознакомится как со статьёй, так и с комментариями к ней, в частности, в которых уже подробно разобрали случаи с Array.prototype.fill().

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

Зачем в 2019 писать void 0?

Статья обновлена: помимо несущественных правок и изменения оформления, добавлено немного информации про работу метода Array.prototype.map(). Если кому интересно, можете ознакомиться.
Спасибо.
Только сейчас обратил внимание, что несмотря на то, что указано, что метод

const matrix = new Array(5).fill(
  new Array(5).fill()
)
или в такой форме
[...Array(5)].fill([...Array(5)])

приведенный napa3um'ом(тут ) и qbz'ом (тут) соответственно, не рабочий (а подметил это TheShockздесь и здесь)), по причине:

const matrix = [...Array(5)].fill([...Array(5)]);
matrix[1][2] = 42;

console.log(matrix);
/*
(5) […]
​0: Array(5) [ undefined, undefined, 42, … ]
​1: Array(5) [ undefined, undefined, 42, … ]
​2: Array(5) [ undefined, undefined, 42, … ]
​3: Array(5) [ undefined, undefined, 42, … ]
​4: Array(5) [ undefined, undefined, 42, … ]
​length: 5
*/

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

Дело в том, что используя функцию Array.prototype.fill() в качестве заполнителя, мы должны учитывать один немаловажный нюанс: если мы передаём ей объект, коим безусловно является и массив, то передаётся не n отдельных экземпляров, а 5 ссылок на один объект, и, когда мы выполняем операцию

arr[1][2] = 42;

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

Для примера и наглядности:

const matrix = [...Array(5)].fill({test:""});
console.log(matrix);
/*
(5) […]
​0: Object { test: "" }
​1: Object { test: "" }
​2: Object { test: "" }
​3: Object { test: "" }
​4: Object { test: "" }
​length: 5
*/

matrix[0].test = 42;
console.log(matrix);
/*
(5) […]
​0: Object { test: 42 }
​1: Object { test: 42 }
​2: Object { test: 42 }
​3: Object { test: 42 }
​4: Object { test: 42 }
​length: 5
​*/

Ну, а когда мы используем Array.prototype.fill() в функции вроде

const matrix = Array.from(Array(5), () => Array(5).fill())

мы имеем пять различных объектов и получаем на выходе корректную матрицу, так как Array.prototype.fill() вызывается 5 раз (по разу на каждый элемент) находясь в map функции метода Array.from().

Конечно, для кого то все эти вещи могут показаться очевидными, но, цитируя одного из упомянутых выше комментаторов: «просто для полноты картины», решил закрыть этот подвисший вопрос, и, возможно, кому-то это по итогу может оказаться полезным.
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации