Как определить, когда использовать объект для параметров функции, а когда нет?
Что проверяют
Есть ли у вас критерий, а не вкусовщина: читаемость вызова, развитие API, типизация. Следом во фронтенде часто спрашивают про React: почему объект опций в зависимостях useEffect запускает эффект на каждом рендере.
Как отвечать
Объект параметров с деструктуризацией уместен, когда:
- параметров больше двух-трёх или их легко перепутать:
resize(300, 200)читается хуже, чемresize({ width: 300, height: 200 }); - есть булевы флаги: вызов
createUser(name, true, false)без сигнатуры перед глазами не понять; - много необязательных параметров: не нужно передавать
undefined, чтобы добраться до пятого аргумента; - API будет расти: новое поле в объекте не ломает существующие вызовы, а новый позиционный аргумент в середине ломает.
Позиционные параметры лучше, когда аргументов один-два и смысл очевиден (formatPrice(amount), clamp(value, min, max)), и для колбэков со стандартной сигнатурой вроде (item, index) в map.
Частый компромисс: обязательное позиционно, опции последним аргументом-объектом. Так устроены fetch(url, init) и addEventListener(type, listener, options). В TypeScript это выглядит так:
function search(query: string, { limit = 20, signal }: SearchOptions = {})
= {} в конце нужен, чтобы вызов search(query) без опций не падал с TypeError на деструктуризации undefined. Бонус TypeScript: у объектного литерала работает проверка лишних свойств, опечатка в имени опции сразу станет ошибкой компиляции.
Аргумент про производительность почти всегда мимо: создание маленького объекта копеечное, и думать о нём стоит только в горячем цикле, подтверждённом профилировщиком.
Где спотыкаются
Забывают = {} у необязательного объекта, и функция падает при первом вызове без опций.
Не знают, что дефолт при деструктуризации срабатывает только на undefined. Придёт { limit: null }, и limit станет null, а не 20.
Создают объект опций прямо в рендере и передают его в хук, который кладёт его в массив зависимостей. Ссылка новая на каждом рендере, и эффект или useMemo пересчитываются каждый раз. Выход: класть в зависимости примитивы или мемоизировать объект.