Какова сложность основных операций с объектами в JavaScript
Что проверяют
Понимаете ли вы, что объект в JavaScript по сути хэш-таблица, которую движок ускоряет скрытыми классами. Следом обычно просят сравнить объект с Map или найти квадратичную сложность в коде с reduce.
Как отвечать
Базовая таблица:
- чтение и запись свойства: O(1) в среднем. В V8 у объекта есть скрытый класс, и если в месте вызова всегда объекты одной формы, свойство читается по известному смещению через inline cache, почти как поле структуры;
- добавление свойства: O(1), но меняет скрытый класс. Объекты, которым свойства добавляли в разном порядке, получают разные формы, и код с ними работает медленнее;
delete: формально O(1), но часто переводит объект в словарный режим, где свойства лежат в обычной хэш-таблице и быстрые пути перестают работать. Проще присвоитьundefinedили собрать новый объект;- проверка наличия:
Object.hasOwn(obj, key)O(1), аkey in objи чтение идут по цепочке прототипов, стоимость растёт с её глубиной. Отсутствующее свойство ищется до самогоObject.prototype; - перебор:
Object.keys,Object.entries,for...inза O(n), причёмObject.keysсоздаёт новый массив, аfor...inзахватывает и перечисляемые свойства прототипов; - копирование:
{ ...obj }иObject.assignO(n) по числу свойств.
Отдельно скажите про Map. Для словаря с частыми добавлениями и удалениями, ключами любого типа и заранее неизвестным набором ключей он подходит лучше: удаление не ломает оптимизации, а map.size берётся за O(1), тогда как Object.keys(obj).length строит массив за O(n).
И оговорка: спецификация ECMAScript сложность не фиксирует, сказанное относится к V8, другие движки устроены похоже.
Где спотыкаются
Пишут reduce((acc, x) => ({ ...acc, [x.id]: x }), {}). Каждая итерация копирует накопленный объект, и на 10 000 элементов это десятки миллионов операций. Нужно мутировать аккумулятор или использовать Object.fromEntries.
Считают delete бесплатной операцией и удаляют свойства в горячем цикле, после чего объект уходит в словарный режим.
Проверяют размер через Object.keys(obj).length внутри цикла и превращают линейный код в квадратичный.