PassFront
  1. PassFront
  2. База знаний
  3. Массивы и списки

Как устроены массивы в JavaScript под капотом: packed, holey и elements kinds в V8?

Массивы и списки

JS-массив — это объект, но V8 хранит его индексированные элементы отдельно в плотном буфере и помечает «видом элементов» (elements kind): от него зависит, какие оптимизации применит JIT.

Основные виды

  • PACKED_SMI_ELEMENTS — только малые целые (Smi), самый быстрый вариант.
  • PACKED_DOUBLE_ELEMENTS — числа с плавающей точкой, хранятся «распакованными» doubles.
  • PACKED_ELEMENTS — произвольные значения: строки, объекты, смесь типов.
  • HOLEY_* — те же виды, но с дырами: при чтении дыры движок обязан проверить цепочку прототипов.
  • DICTIONARY_ELEMENTS — разреженный массив в виде хеш-таблицы, например после arr[1e7] = 1 на пустом массиве.

Переходы только в одну сторону

  • [1, 2] → push(1.5) → DOUBLE → push('a') → ELEMENTS.
  • Удаление строки или дыры не вернёт массив в SMI или PACKED: вид «портится» навсегда.
  • new Array(3) сразу создаёт HOLEY-массив, даже если потом заполнить все ячейки.

Практические выводы

  • Не создавайте дыры и не читайте за границей массива (arr[arr.length]): это уводит в медленный путь.
  • Держите массив однотипным: числа отдельно от объектов.
  • -0, NaN и Infinity не являются Smi — один такой элемент переводит массив в DOUBLE.

Коротко для собеседования

V8 оптимизирует массивы по виду элементов, и вид может только ухудшаться: смешали типы или создали дыру — массив навсегда уходит в более медленный режим. На практике заполняйте массивы подряд, держите их однотипными и не читайте за границей; проверить вид можно через %DebugPrint с флагом --allow-natives-syntax.

Другие вопросы раздела