Может ли при push в массив в JavaScript происходить копирование данных
Что проверяют
Различаете ли вы две вещи: что push делает со значениями и что движок делает с памятью массива. Хороший ответ разводит их явно. Следом обычно спрашивают про иммутабельное обновление массива в React и его цену.
Как отвечать
С точки зрения языка push ничего не копирует глубоко. Массив хранит значения: примитив лежит как значение, объект как ссылка на тот же объект. Поменяете obj.name после arr.push(obj), и изменение будет видно через arr[0].
На уровне движка копирование бывает. В V8 элементы массива лежат в отдельном хранилище с запасом ёмкости:
- пока запас есть:
pushпишет в следующую ячейку за O(1); - когда место кончилось: движок выделяет хранилище примерно в полтора раза больше и копирует туда все элементы. Отдельный такой
pushстоит O(n), но в среднем операция остаётся амортизированной O(1); - смена вида элементов: V8 различает массивы из малых целых, из дробных чисел и из произвольных значений. Добавили
1.5в массив целых или строку в массив чисел, и хранилище может быть перестроено в более общий вид.
Практические выводы. Не создавайте дырки вроде arr[1000] = x на пустом массиве: массив становится «дырявым», и доступ к нему медленнее. Для больших числовых данных известного размера подходят типизированные массивы вроде Float64Array: длина фиксирована, перераспределений нет.
В React push мутирует массив, ссылка не меняется, и setState с тем же массивом не вызовет перерисовку. Там пишут [...items, item], а это уже копирование всех элементов, O(n) на каждое добавление. Копирующие методы toSorted, toSpliced и with появились в ES2023.
Где спотыкаются
Говорят, что при push объект копируется. Копируется только ссылка, для независимой копии нужен structuredClone или ручное клонирование.
Отрицают копирование полностью. Перераспределение хранилища при росте реально и видно в профайлере на миллионах элементов.
Пишут arr.push(...hugeArray). Аргументы передаются через стек, и на сотнях тысяч элементов это RangeError: Maximum call stack size exceeded. Для слияния надёжнее concat или цикл.