Массивы и коллекции
Методы массивов и мутация, семантика сортировки и копирования, Map и Set, WeakMap и WeakSet, типизированные массивы и разреженные массивы.
13 вопросов
JuniorТеорияОчень частоЧто делает каждый из методов массива map, filter, reduce и forEach?
Что делает каждый из методов массива map, filter, reduce и forEach?
map возвращает новый массив той же длины, преобразуя каждый элемент. filter возвращает новый массив, оставляя только элементы, для которых колбэк вернул истинное значение. reduce сворачивает все элементы в одно накопленное значение через редьюсер и начальное значение. forEach просто вызывает колбэк для каждого элемента и возвращает undefined — он нужен для побочных эффектов, а не для построения результата.
Типичные ошибки
- ✗Использовать
mapради побочных эффектов, где нуженforEach— тогдаmapстроит массив изundefined - ✗Ожидать, что
forEachвернёт значение или будет цепочечным, какmap/filter - ✗Забывать, что
reduceтребует начального значения, чтобы безопасно обработать пустой массив
Уточняющие вопросы
- →Почему
forEachигнорирует значение, которое выreturnиз его колбэка? - →Как выразить и
map, иfilterчерез один толькоreduce?
JuniorТеорияОчень частоВ чём разница между методами массива slice и splice?
В чём разница между методами массива slice и splice?
slice(start, end) не мутирует: возвращает новую поверхностную копию выбранного диапазона, не трогая исходный массив. splice(start, deleteCount, ...items) изменяет оригинал на месте — удаляет, вставляет или заменяет элементы — и возвращает массив удалённых элементов. То есть slice нужен для чтения подмассива, а splice — для редактирования самого массива.
Типичные ошибки
- ✗Путать их и мутировать массив через
splice, когда нужна была копия черезslice - ✗Думать, что оба возвращают одно —
spliceвозвращает удалённые,sliceвозвращает скопированный подмассив - ✗Забывать, что
spliceумеет ещё и вставлять/заменять элементы, а не только удалять
Уточняющие вопросы
- →Как с помощью
spliceвставить элементы, ничего не удаляя? - →Почему копию, возвращаемую
slice, называют поверхностной?
JuniorТеорияЧастоКак Array.prototype.sort упорядочивает элементы по умолчанию и как правильно сортировать числа?
Как Array.prototype.sort упорядочивает элементы по умолчанию и как правильно сортировать числа?
По умолчанию sort приводит каждый элемент к строке и сравнивает их по кодовым единицам UTF-16, поэтому [10, 2, 1] становится [1, 10, 2] — лексикографически, а не численно. Чтобы правильно отсортировать числа, передают компаратор (a, b) => a - b: отрицательный результат ставит a раньше, положительный — b раньше, ноль сохраняет порядок. sort также мутирует массив на месте и возвращает его.
Типичные ошибки
- ✗Сортировать числа без компаратора и получать лексикографический порядок вроде
[1, 10, 2] - ✗Возвращать из компаратора булево вместо отрицательного/нуля/положительного числа
- ✗Считать, что
sortвозвращает новый массив — он мутирует оригинал на месте
Уточняющие вопросы
- →Что означает знак возвращаемого компаратором значения для порядка двух элементов?
- →Как отсортировать по убыванию или массив объектов по полю?
MiddleТеорияЧастоЧто возвращает каждый из методов перебора массива some, every, find, findIndex и reduce?
Что возвращает каждый из методов перебора массива some, every, find, findIndex и reduce?
some возвращает true, если хотя бы один элемент проходит предикат; every возвращает true только если проходят все (и true для пустого массива). find возвращает первый подходящий элемент или undefined; findIndex — его индекс или -1. reduce сворачивает массив в одно накопленное значение. some, every и find прекращают перебор, как только результат определён.
Типичные ошибки
- ✗Менять местами
someиevery—someэто «хоть один»,everyэто «все» - ✗Ожидать, что
findвернёт индекс или булево вместо элемента - ✗Считать, что
everyна пустом массиве этоfalse— он возвращаетtrueпо пустоте
Уточняющие вопросы
- →Почему
everyвозвращаетtrueдля пустого массива, аsome—false? - →Как короткое замыкание в
some/findвлияет на производительность на больших массивах?
MiddleТеорияЧастоЧем Map отличается от обычного объекта и чем он отличается от Set?
Чем Map отличается от обычного объекта и чем он отличается от Set?
В отличие от обычного объекта, Map принимает ключом любое значение (не только строки и символы), гарантирует порядок вставки, имеет size, напрямую итерируется и не имеет ключей из прототипа, с которыми можно столкнуться. Объект проще, сериализуется в JSON и годится для фиксированных строковых ключей. В отличие от Set: Map хранит пары ключ→значение, а Set хранит лишь уникальные одиночные значения без связанного значения — поэтому Set для проверки членства, Map для поиска по ключу.
Типичные ошибки
- ✗Использовать объект, где нужны нестроковые ключи — только
Mapподдерживает произвольные ключи - ✗Забывать, что ключи объекта могут столкнуться с унаследованными именами вроде
toString - ✗Думать, что
Setхранит пары ключ→значение — он хранит лишь одиночные уникальные значения
Уточняющие вопросы
- →Когда обычный объект всё же лучше
Map? - →Как преобразовать
Mapв обычный объект и обратно?
MiddleТеорияЧастоКакие методы массива мутируют исходный массив, а какие вместо этого возвращают новый?
Какие методы массива мутируют исходный массив, а какие вместо этого возвращают новый?
Мутирующие методы меняют массив на месте: push, pop, shift, unshift, splice, sort, reverse и fill. Немутирующие возвращают новый массив, не трогая оригинал: map, filter, slice, concat и копирующие методы ES2023 toSorted, toReversed, toSpliced и with. Мутирующие часто возвращают другое — push возвращает новую длину, pop — удалённый элемент — тогда как немутирующие возвращают новый массив.
Типичные ошибки
- ✗Считать, что
sortиreverseвозвращают копию — они мутируют на месте и возвращают тот же массив - ✗Забывать, что
push/popвозвращают длину/элемент, а не массив, поэтому не цепляются, какmap - ✗Путать
slice(копия) соsplice(мутация)
Уточняющие вопросы
- →Как методы ES2023
toSorted/toReversedсоотносятся сsort/reverse? - →Почему мутирующий метод вроде
sortможет вызвать баги в коде, делящем ссылку на массив?
SeniorТеорияЧастоКакие приёмы позволяют преобразовать массив, не мутируя оригинал?
Какие приёмы позволяют преобразовать массив, не мутируя оригинал?
Предпочитайте методы, возвращающие новый массив: map, filter, slice, concat и spread [...arr]. Копирующие аналоги мутаторов из ES2023 — toSorted, toReversed, toSpliced и with — заменяют sort/reverse/splice и присваивание по индексу, не трогая исходник. reduce строит производную структуру за один проход. Когда есть только мутатор, сначала копируют, затем мутируют копию: const sorted = [...arr].sort(cmp). Эти приёмы важны для неизменяемого состояния во фреймворках вроде React.
Типичные ошибки
- ✗Вызывать
sort/reverseна общем состоянии, мутируя его под другими держателями ссылки - ✗Думать, что spread
[...arr]делает глубокое клонирование — это поверхностная копия - ✗Хвататься за
JSON.parse(JSON.stringify(...)), когда хватаетmap/slice/toSorted
Уточняющие вопросы
- →Почему неизменяемое состояние важно для обнаружения изменений во фреймворках вроде React?
- →Как неизменяемо обновить один элемент по индексу без
with?
JuniorТеорияИногдаЧто такое Map и чем он отличается от обычного объекта для хранения пар ключ–значение?
Что такое Map и чем он отличается от обычного объекта для хранения пар ключ–значение?
Map — встроенная коллекция с ключами, где ключом может быть любое значение — объекты и функции, а не только строки и символы. Он помнит порядок вставки, имеет свойство size и напрямую итерируется через for...of, .keys(), .values() и .entries(). Чтение и запись — через get, set, has и delete. У него нет ключей из прототипа, поэтому пользовательские ключи никогда не сталкиваются с унаследованными свойствами.
Типичные ошибки
- ✗Считать, что ключи
Mapприводятся к строкам, как ключи объекта — ключом служит любое значение - ✗Использовать
map.lengthвместо свойстваsizeдля подсчёта записей - ✗Забывать, что
Mapсохраняет порядок вставки при итерации
Уточняющие вопросы
- →Как
Mapсравнивает объектные ключи — по ссылке или по структурному содержимому? - →Почему
Mapнельзя напрямую сериализовать черезJSON.stringify?
JuniorТеорияИногдаЧто такое Set и как с его помощью убрать дубликаты из массива?
Что такое Set и как с его помощью убрать дубликаты из массива?
Set — встроенная коллекция уникальных значений: добавление уже имеющегося значения игнорируется, поэтому дубликатов в нём не бывает. Уникальность определяется по SameValueZero, поэтому NaN считается одним значением. Он итерируется в порядке вставки и имеет свойство size. Чтобы убрать дубликаты массива, оборачивают его в Set и разворачивают обратно: [...new Set(arr)], сохраняя первое вхождение каждого значения.
Типичные ошибки
- ✗Думать, что
Setиспользует==для уникальности — он использует SameValueZero, поэтому1и'1'различны - ✗Считать, что
NaNможет попасть вSetдважды — он засчитывается как одно значение - ✗Забывать развернуть
Setобратно в массив, когда нужен результат-массив
Уточняющие вопросы
- →Как
Setрешает, что два значения равны — что такое SameValueZero? - →Почему
[...new Set(arr)]сохраняет первое вхождение каждого дубликата?
SeniorТеорияИногдаКакие тонкие гарантии управляют Array.prototype.sort — стабильность, мутация, компаратор, дыры?
Какие тонкие гарантии управляют Array.prototype.sort — стабильность, мутация, компаратор, дыры?
По умолчанию sort лексикографичен (элементы приводятся к строкам и сравниваются по UTF-16), поэтому числам нужен (a, b) => a - b. С ES2019 sort гарантированно стабилен: равные элементы сохраняют относительный порядок. Он мутирует массив на месте и возвращает ту же ссылку. Компаратор должен быть согласованным — возвращать отрицательное/ноль/положительное строго как функцию двух аргументов — иначе порядок зависит от реализации. Значения undefined и дыры никогда не передаются компаратору и всегда уходят в конец.
Типичные ошибки
- ✗Считать
sortнестабильным — он гарантированно стабилен с ES2019 - ✗Возвращать из компаратора булево вместо знакового числа
- ✗Считать, что
undefined/дыры сортируются компаратором — они уходят в конец нетронутыми
Уточняющие вопросы
- →Почему несогласованный компаратор (например, случайный возврат) даёт неопределённый порядок?
- →Как отсортировать, не мутируя исходник — какой метод или копию вы используете?
MiddleТеорияРедкоВ чём разница между плотными и разреженными массивами и как методы перебора обходятся с дырами?
В чём разница между плотными и разреженными массивами и как методы перебора обходятся с дырами?
Плотный массив имеет значение на каждом индексе от 0 до length - 1; разреженный имеет дыры — отсутствующие индексы, созданные [1, , 3], Array(3), delete arr[i] или увеличением length. Дыра — не то же, что элемент, равный undefined. Методы вроде forEach, map, filter и reduce полностью пропускают дыры (колбэк там не вызывается), а map сохраняет дыры в результате. Чтение дыры даёт undefined, поэтому in или hasOwnProperty — надёжный способ её обнаружить.
Типичные ошибки
- ✗Считать дыру тождественной элементу, равному
undefined - ✗Ожидать, что
forEach/mapвызовут колбэк на каждом индексе разреженного массива - ✗Считать, что
mapсхлопывает дыры — на деле он сохраняет их в результате
Уточняющие вопросы
- →Как отличить дыру от настоящего
undefined-элемента на индексе? - →Какие новые механизмы вроде
Array.fromили spread трактуют дыры какundefined, а не пропускают?
MiddleТеорияРедкоЧто такое типизированные массивы и как ArrayBuffer и представления вроде Uint8Array работают вместе?
Что такое типизированные массивы и как ArrayBuffer и представления вроде Uint8Array работают вместе?
ArrayBuffer — это блок сырых двоичных байтов фиксированной длины, который нельзя читать напрямую. Типизированный массив вроде Uint8Array или Float64Array — это представление, трактующее эти байты как числа фиксированного типа и ширины. Несколько представлений могут разделять один буфер, поэтому запись через одно меняет байты, видимые другим. Типизированные массивы имеют фиксированную длину и хранят только числа — они лежат в основе двоичных данных: файлов, сетевых кадров, пикселей WebGL/canvas.
Типичные ошибки
- ✗Думать, что
ArrayBufferможно индексировать напрямую — нужно представление над ним - ✗Считать представления над одним буфером независимыми — они делят байты
- ✗Ожидать, что типизированные массивы растут или хранят нечисловые значения, как обычный массив
Уточняющие вопросы
- →Чем
DataViewотличается от типизированного представления над тем же буфером? - →Что означает порядок байтов при чтении многобайтового числа из буфера?
MiddleТеорияРедкоЧто такое WeakMap и WeakSet и чем они отличаются от Map и Set?
Что такое WeakMap и WeakSet и чем они отличаются от Map и Set?
Ключи WeakMap и элементы WeakSet должны быть объектами (или символами) и удерживаются слабо: если на объект больше нет других ссылок, он может быть собран сборщиком мусора, и запись исчезает автоматически. Поскольку членство может пропасть в любой момент, они не итерируются, не имеют size и не очищаются перебором. Они дают лишь get/set/has/delete (add/has/delete у WeakSet). Типичное применение — привязка приватных метаданных к объектам без утечки памяти.
Типичные ошибки
- ✗Пытаться использовать примитив ключом
WeakMap— ключи должны быть объектами или символами - ✗Ожидать, что можно перебрать
WeakMap/WeakSetили прочитать егоsize - ✗Думать, что слабые ссылки удерживают объект живым — они делают наоборот
Уточняющие вопросы
- →Почему невозможность итерации вытекает из недетерминированности слабых ссылок?
- →Как
WeakMapпомогает реализовать по-настоящему приватные данные экземпляра?