Всем привет. Сегодня я вернулся с отпускной поездки, проснулся, сделал себе кофеек, заказал завтрак, и решил глянуть наконец-то вышедший State Of JS 2023.

Читаю, открываю секцию библиотек - и первое, что я вижу

50% и топ 1. За что?
50% и топ 1. За что?

33% момента тоже ужасают (про jquery молчу) - но давайте поговорим про лодаш.

Я последние годы являюсь его ярым антагонистом. Я считаю, что многие либо используют его неправильно, либо используют без смысла - а для новичков он даже может быть вреден. Попробую объяснить свою позицию - поехали!

1. Установка

Первое, чем нас встречает лодаш - набором библиотек. У вас есть выбор:

  • Установить основной лодаш целиком (но он может криво работать на ласт вебпаке и тем более Vite - и ругаться на ESM)

  • Установить отдельно пакеты - но они не обновляются и потом будут депрекейтнуты

  • Установить lodash-es, но он тоже может криво работать на разных версиях вебпака или окружениях (SSR/CSR), иногда придется пострадать со странными ошибками - просто другими, нежели с обычной версией

  • lodash-fp, но он депрекейтет 8 лет назад

  • lodash-amd и использовать AMD лоадер (кто-то еще так делает?). И пакет не обновлялся еще больше основного

Помимо этого, вам, в идеале, надо настроить плагин бейбла и вебпак) На выбор представлено множество вариантов кода

Мммм, node 6
Мммм, node 6

Итого, вы скорее всего не сможете установить и использовать лодаш одной командой инсталла - на всех проектах уж точно.

Кроме того, вот эти отдельные бандлы вызывают проблемы - иногда хочется инстиктивно установить только то, что нужно, но они могли не обновляться более пяти лет - и содержать уязвимости. А предупреждения от разработчиков никто не читает, конечно.

А если сможете - см. пункт 2.

2. Использование

Итак, что нам рекомендует документация:

Если мы будем следовать этой рекомендации, мы подключим целиком весь бандл лодаша. Минифицированный бандл на CDN весит 72 килобайта. Вроде и немного, но грузить все методы браузеру тоже будет грустновато.

Ну ладно! Что у нас там дальше:

Итак, что из этого нам выбрать?

  1. Полную сборку грузить, наверное, не очень

  2. Неплохо выглядит core, но в документации нет информации, что туда входит

  3. Для чего нужен FP - никто не поймет

  4. Категории методов грузить удобно, но там их тоже куча + надо смотреть доку

К чему я веду. Представьте - вы новичок и зашли сюда, вам нужно по бырику взять метод глубокого клонирования. Вы смотрите на доку и ничего не понимаете. Что вы сделаете?

Правильно. Скорее всего вы подгрузите весь lodash, в лучшем случае - core. Также допустимы ошибки по типу загрузить отдельный npm пакет - к чему это может привести писал в конце первого раздела.

По итогу, большинство проектов грузят весь лодаш целиком, хотя им нужны оттуда методов пять. При условии корректной настройки babel, webpack (или другого сборщика), у вас есть шанс получить работающий tree-shaking, которого нет из коробки по умолчанию. lodash-es должен работать нормально, но это при условии, что не возникнет проблем при его установке - или разработчик не сдастся их решать.

Неконсистентность пакетов и подключения вызывает такие запросы в топе гугла по запросу "lodash esm":

Круто, не правда ли? Но мы идем дальше!

3. Вы используете из него 5 методов

Конечно, это применимо не ко всем проектам - в основном, к старым. Но невероятно част тот кейс, когда разработчик подключает лодаш ради... одной-двух функций.

В современном стеке, ОЧЕНЬ много методов лодаша уже было портировано. Я люблю задавать вопрос на собесе - как глубоко склонировать объект? Больше половины, особенно новичков, называют "лодаш дипклон" - что, на мой взгляд, является проблемой, при условии, что существует structuredClone, который имеет неплохую поддержку и полифиллы, а также на крайняк куча легковесных современных библиотек.

По итогу, lodash во многих случаях позволяет вам делать то, что УЖЕ поддерживается в JS... но хуже. Понятно, что некоторые методы "незаменимы", но возьмем несколько примеров:

  • array.drop -> slice

  • array.concat -> filter

  • array.fill -> splice

  • findIndex/findLastIndex/indexOf/join/reverse/slice -> натив

  • flatten -> flatMap

Ну и так далее. Но вы скажете, что же насчет незаменимых методов? Давайте посмотрим на те, что я видел чаще всего:

  • debounce -> это несколько строк кода. Вы можете написать свой крохотный метод с удобным API и так, как вам нужно

  • delay -> это вообще setTimeout, лучше написать свой асинхронный sleep из 5 строк кода

  • clone -> structuredClone покрывает почти всё, кроме чего-то реактивного

    • Это один из тех методов, который я не могу рекомендовать как 100% заменимый - я бы его заменял библиотекой для клонирования - по типу https://www.npmjs.com/package/klona

  • difference -> нормальных замен нет, но есть замены, по типу deep-object-diff и других (и там не очень много кода! Мы, например, написали свой метод на основе deep-object-diff)

В остальном, по моим наблюдением, другие методы либо не используются, либо могут быть весьма легко заменены нативными.

4. modern - это ложь

На сайте lodash указано, что он modern - что далеко от истины. Лодаш не использует современные сборщики, не обновляется годами, множество методов есть в нативе (и нативные, вероятно, будут быстрее - и не жрать место/память как минимум).

Кроме того, при использовании аналогов из лодаша вместо натива вы упускаете возможность следить за тем, что добавляется в натив - а там много всего очень интересного! Всякие toSorted, новые методы коллекций - можно использовать в ноде, можно обмазаться полифиллами (смотря какие версии браузеров вам нужны). Использование этих методов в том числе прокачивает ваше знание как джаваскриптера. И для этой прокачки вам точно не нужен лодаш.

Резюме

Лодаш:

  • Имеет множество методов установки, каждый из которых может вызывать проблемы и головную боль с подключением

  • Легко подключается целиком вместо нескольких нужных вам методов

  • Не обновляется

  • Не нужен в 95% случаев

  • Мешает пониманию действительно современных методов

Вам не нужен лодаш. Пройдитесь по своим методам, выкиньте доступные в нативе, замените недоступные (если они вам точно нужны).

А если уж вы его используете:

  • Используйте es-версию с костылями под TS, если потребуются, или обычную с плагинами для tree-shaking

  • Не используйте эти отдельные пакеты - только lodash или lodash-es

  • Не увеличивайте его процент использования в вашем коде. Пожалуйста

  • Вы может быть еще и Moment используете? Тоже пора обновляться!

Хороший пример

Хотел бы показать прекрасных ребят из UnJS, создавшие набор un-iversal библиотек: https://unjs.io

Почему это топ:

  1. Действительно современный и очень легковесный код с минимум сборки

  2. Декомпозированные пакеты - берете то, что нужно

  3. Не нужно ставить всякие плагины babel/ts - всё работает из коробки, как только импортнете. Установил и используй!

Я давненько хотел сделать эту статью. Есть тут любители lodash? Поделитесь, продолжите ли вы его использовать после всех пунктов выше - и почему? А если вы давно хотели уйти, но не хватало аргументов - ловите :)

И читаем State Of JS 2023: https://2023.stateofjs.com/en-US (я написал резюме на русском в своем тг канале: https://t.me/webdanil/137. Это же можно так делать, да? Вроде не пиарюсь... почти...)

Комментарии (46)


  1. daniluk4000 Автор
    21.06.2024 09:10
    +5

    А, ну и бонусный комментарий: пожалейте разработчиков, которые не используют лодаш. Я сталкивался с лодашными проектами: читать этот код больно, местами ты не понимаешь, зачем нужно вот это, а что делает вот это. Документация у них не идеальная, и, опять же, новички могут его использовать совсем уж неправильно.

    В отличие от всяких библиотек, интегрированных в фреймворк, лодаш совсем не выглядит нативно - особенно с этим чудесным андерскором.


    1. KLIJIN
      21.06.2024 09:10

      Я в 2024 году просто копирую кусок кода в chart GPT с запросом "объясни подробно этот код"

      И gpt возвращает длинную простыню с человекочитаемым объяснением. Только это много времени отнимает


    1. mazdai19
      21.06.2024 09:10

      хорошая статья, нужна такая же про моментс


      1. daniluk4000 Автор
        21.06.2024 09:10

        Не нужна, потому что он официально устарел) Там нет особо дискуссии


  1. aleks_raiden
    21.06.2024 09:10
    +2

    Вот я как пример пользователя лодаша (правда на сервер-сайде). Использую: _.noop, _.clone, _.clamp, _.min / _.max, _.compact а также более удобные обертки типа isNaN / isInteger. Удобно и минималистично ) зачем мне самописно заменять все, если одна маленькая и многолетне вылизанная либа все покрывает )


    1. daniluk4000 Автор
      21.06.2024 09:10
      +3

      Не сказал бы что совсем маленькая) Минмакс есть в жс, isnan есть в Number.isnan, за остальное не подскажу с ходу, но все же - смысла не вижу


  1. artptr86
    21.06.2024 09:10
    +1

    Ещё одна, не упомянутая в статье, проблема лодаша — по наследству от андерскора функции в нём перегружены для множества различных типов. То есть, условный filter будет обязательно содержать код для фильтрации и объектов, и массивов, да ещё и, из-за перегрузки типа второго аргумента, — код для фильтрации по явному предикату, если будет передана функция, либо по множеству ключей-значений через matches, если будет передан объект; по ключу-значению через matchesProperty, если будет передан массив-кортеж; по значению поля через property. Притом этот код не может быть удалён тришейком.

    Но вы скажете, что же насчет незаменимых методов? Давайте посмотрим на те, что я видел чаще всего

    По моим прикидкам, чаще всего из «незаменимого» встречаются debounce и sortBy.


    1. daniluk4000 Автор
      21.06.2024 09:10

      Спасибо!

      Про методы: дебаунс совсем уж легко заменить, sortBy... Не знаю с ходу отличий от обычного сорта, но как будто мы не так часто сортируем, а когда да, прописывание логики по типу (a,b) => a - b даже помогает в понимании (имхо)


      1. artptr86
        21.06.2024 09:10
        +1

        Отличие в том, что sortBy позволяет делать сортировку по нескольким полям одновременно. Например, sortBy(users, ['lastName', 'firstName'])


        1. artptr86
          21.06.2024 09:10

          Можно написать собственную версию где-нибудь в utils, но даже этот код можно долго улучшать для поддержки направлений сортировки, чисел параллельно со строками или для натуральной сортировки строк с числами:

          function comparator(keys) {
            const coll = new Intl.Collator('ru');
            
            return (a, b) => keys.reduce((acc, key) => (acc || coll.compare(a[key], b[key])), 0);
          }
          
          // users.toSorted(comparator(['lastName', 'firstName']))

          Так что это хороший кандидат на выделение в библиотеку.


          1. daniluk4000 Автор
            21.06.2024 09:10
            +1

            Тут соглашусь, звучит не так легко. Просто сам никогда этим не пользовался, но фишка удобная


  1. sfi0zy
    21.06.2024 09:10
    +7

    В целом я со всем согласен, но

    Вы можете написать свой крохотный метод с удобным API и так, как вам нужно

    Приводит к тому, что в разных местах проекта плодятся крохотные, почти одинаковые, но не совсем, методы, от разных авторов, которые делают вроде одно и то же, но не совсем. Это такой зоопарк, от которого увеличивается вероятность логических ошибок, которые сложно отлавливать. Нужно постоянно в новый контекст влезать. Плюс дублирующийся код, который порой сложно просто так сократить в дальнейшем из-за едва различимой разницы во внутренней логике. Тут нужно сильно заморачиваться с ревью и документацией, чтобы этого не допустить. Но это тоже время, да и вечные споры, как какую помогалку лучше сделать и назвать. Библиотеки - сборники утилит, вроде lodash или D3, дают тот же эффект, что и инструменты по типу prettier - тимлид приходит, бъет кулаком в стол и говорит "мы делаем так!". И все, больше никакого зоопарка. Это далеко не идеальный вариант, но тем не менее проблему синхронизации всех участников решает.


    1. artptr86
      21.06.2024 09:10

      Это не совсем решает вопрос с наличием аналогичных встроенных методов. Например, делать map/filter/toSorted массива через нативную версию или через Lodash?


    1. daniluk4000 Автор
      21.06.2024 09:10
      +2

      Скажу как тимлид, ударивший кулаком: написали свой UI Kit c утилитами и юзаем. Полет - прекрасный)


  1. Lazytech
    21.06.2024 09:10
    +7

    1. dom1n1k
      21.06.2024 09:10

      Первая же функция в списке реализована адски неэффективно


      1. Lazytech
        21.06.2024 09:10
        +1

        Каюсь, читал по верхам. Да, согласен, многократное применение деструктуризации массива не способствует высокой производительности.

        Разумеется, существуют другие варианты реализации такой функции:
        javascript - Split array into chunks - Stack Overflow

        Еще кое-что:
        You Might Not Need Lodash
        Альтернативная реализация chunk (вроде тоже не очень эффективная?)


        1. dom1n1k
          21.06.2024 09:10

          Внутри Lodash хорошая реализация. По вашей ссылке на SO топовый ответ тоже хороший (на самом деле там только цикл другой, а базовая схема та же).

          Я немного поэкспериментировал, сравнил несколько вариаций с разными циклами и немного разными подходами - все более-менее близки, максимальный разбег примерно x3. Но вот та реализация с деструктуризациями на каждой итерации - катастрофа. Если массив более-менее большой, она в сотни и тысячи раз медленнее, на несколько минут(!) может залипнуть.


  1. molnij
    21.06.2024 09:10

    Даже любопытно стало, а вместо момента вы что предлагаете? Придуманный инопланетянами Intl?


    1. artptr86
      21.06.2024 09:10

      А для каких целей: форматировать или проводить операции с датой/временем?


      1. Elendiar1
        21.06.2024 09:10

        dayjs


    1. daniluk4000 Автор
      21.06.2024 09:10
      +1

      В целом что угодно современное. Мы используем Intl и натив, но если не подходит, сейчас date-fns очень популярен слышал. А так Moment просто официально умер, поэтому лучше бы слезать


      1. Orimay
        21.06.2024 09:10

        Почему не Temporal?


        1. daniluk4000 Автор
          21.06.2024 09:10

          Поддержка низкая

          Не низкая, её нет


    1. Dark_zarich
      21.06.2024 09:10
      +2

      date-fns или day.js


    1. AngusMetall
      21.06.2024 09:10

      Luxon вполне себе.


    1. KLIJIN
      21.06.2024 09:10

      date-fns


  1. sumdy-c
    21.06.2024 09:10

    Меня возьмут в рай если я буду клонировать объект так:

    const obj = { test: { test: { test: 'Hi!' } } };
    const cloneObj = JSON.parse(JSON.stringify(obj));

    Я бы взял. Ну лучше пока мир не придумал....


    1. koreychenko
      21.06.2024 09:10
      +1

      Хм.... я вот всегда так делаю. Но мне можно, я бэкендер :-)


      1. artptr86
        21.06.2024 09:10
        +3

        structuredClone уже в Node 17 работает. Почему бы его не использовать?


    1. daniluk4000 Автор
      21.06.2024 09:10
      +2

      Самый надежный метод из придуманных, конечно) Но да, structuredClone - наш выход


    1. bromzh
      21.06.2024 09:10

      Это ок только если ты прям точно уверен, то копируемый объект не будет содержать циклических ссылок.


      1. Gibboustooth
        21.06.2024 09:10

        Или NaN. Или BigInt. Или функций... В целом, прекрасный способ выстрелить себе в ногу.


  1. zurzeropsi
    21.06.2024 09:10
    +2

    Есть мнение, что программирование - оно все о переиспользовании. Переиспользовании проверенных, протестированных и доказавших свою надежность решений вместо написания своих велосипедов.

    Я сталкивался с лодашными проектами: читать этот код больно, местами ты не понимаешь

    А написать по 5-10 строк кода тут-там, забыть что оно уже написано (или не знать, что оно уже написано другим разрабом) и лежит воооон в том дальнем углу огромного проекта, и написать еще разок - весь этот замес будет более читаемым?

    Если плохи дела со сборкой и 24kb gzipped - много, то просто создайте свой ПР, который позволит использовать более современный подход. Вродь же живая репа, мерджи есть. 50 млн только спасибо скажут https://npmtrends.com/lodash, а откажутся ли они после данной статьи - это врядли )


    1. daniluk4000 Автор
      21.06.2024 09:10
      +1

      Я очень ленивый разработчик, поэтому написали свой набор утилит, и локально в проектах переиспользуем composable (я вьюшник) в отдельных папках. И это не велосипеды, а небольшие методы. Велосипеды есть, но их минимум - если совсем выхода нет, подключаем библиотеку. Но ни разу за все время в АВ на куче проектов никто не сказал "это нельзя сделать без лодаша".

      А писать PR и переписывать лодаш - вот опять же, смысл? Ради тех пяти методов? Им всем уже есть современные легковесные альтернативы (если уж нет в нативе), может за редкими исключениями.


    1. Ares_ekb
      21.06.2024 09:10
      +1

      Переиспользовании проверенных, протестированных и доказавших свою надежность решений вместо написания своих велосипедов

      Другая крайность - это вместо велосипеда получить франкенштейна. Навтягивать из разных библиотек однострочных функций. На мой взгляд уж лучше написать у себя эти функции в едином стиле, чем тянуть лишние зависимости в проект


  1. n0ne
    21.06.2024 09:10
    +2

    Любая библиотека - это всего лишь инструмент, каждый сам себе решает, использовать или нет.

    Если платят за написание собственных велосипедов - можно и писать, но они заведомо будут хуже того, что было протестированно на таком количестве тест кейсов, о которых ни вы, ни я даже не слышали.

    В-третьих, люди не работают всю жизнь на одном месте. И никому неизвестные собственные велосипеды всегда будут проигрывать хорошо известным, типа, аналогам.

    Не нравится теущий инструмент - поменял на другой. Со своим велосипедом придется "плакать, но всё равно продолжать есть кактус".

    И такие библиотеки, как lodash, ramda, etc. являются прекрасным обучающим материалом, который просто необходимо просматривать иногда.


    1. daniluk4000 Автор
      21.06.2024 09:10

      можно и писать, но они заведомо будут хуже того, что было протестированно на таком количестве тест кейсов

      Зависит от метода. Условный дебаунс тяжело написать плохо

      можно и писать, но они заведомо будут хуже того, что было протестированно на таком количестве тест кейсов

      Опять же - зависит от сложности и огромности проекта. В большинстве случаев, на больших проектах уже и так куча утилит, а тут просто +5 на замену лодаша, а на мелких часто вам многие методы и не нужны

      Не нравится теущий инструмент - поменял на другой

      Когда весь код покрыт лодашем - так не получится. Мы его относительно долго выпиливали с одного проекта, где он также использовался совершенно бысмысленно

      И такие библиотеки, как lodash, ramda, etc. являются прекрасным обучающим материалом

      Вот совершенно не соглашусь. Их часто используют, не вникая в то, как это работает - методы простые и решают задачу, зачем вникать, пока с ними нет проблем или непоняток?

      В отличие от того же UnJS, которые местами написали сложные штуки, на которые хочется взглянуть - да и то не делают.


      1. n0ne
        21.06.2024 09:10

        Зависит от метода. Условный дебаунс тяжело написать плохо

        Вот как раз дебаунс, хороший, с параметрами, отменой и т.п. написать совсем непросто. Он в библиотеках обычно десятки, а то и сотни LOC

        В большинстве случаев, на больших проектах уже и так куча утилит, а тут просто +5 на замену лодаша, а на мелких часто вам многие методы и не нужны

        Наверно я на недостаточно крупном проекте, в котором только девелоперов 1500 человек от Сиднея до Рейкьявика, но никому и в голову не придет выпиливать простой, удобный и надежный, как лом, lodash

        Мы его относительно долго выпиливали с одного проекта, где он также использовался совершенно бысмысленно

        Если вы на проекте бессмысленно используете инструмент - это разве говорит о том, что плохой инструмент?! Это говорит совсем о другом.

        Вот совершенно не соглашусь.

        Ваше право. Если Вы или Ваши сотрудники не смотрите хоть иногда в код того, что используете, не пытаетесь понять, что-то перенять, чему-то научиться - это очень и очень странно, в свою команду я бы не хотел таких разработчиков.


        P.S. И да, про FP было смешно: я думал речь о разработчиках в статье идет, а не о просто обывателях-домохозяйках.


  1. xemos
    21.06.2024 09:10

    1) structuredClone работает не эквивалентно deepClone, например он теряет конструктор класса, не копирует функции, не копирует геттеры и сеттеры

    2) "Для чего нужен FP - никто не поймет", вы серьёзно?


    1. daniluk4000 Автор
      21.06.2024 09:10

      1. Верно, но на моей практике такие объекты нужно копировать весьма редко

      2. Обыватель - вряд ли. Даже если он доберется для доки - она такая, что проще забить и не использовать (https://github.com/lodash/lodash/wiki/FP-Guide)


  1. rtatarinov
    21.06.2024 09:10

    Я понимаю, что лодаш создает проблемы с тришейкингом, но писать свои утилиты это огромная проблема! Своя утилита должна обрабатывать все кейсы, которые разработчик вряд ли покроет. Это первое! А второе, типы, граждане вывод типов. Все ваши собственные утилиты никогда нормально не умели в сужение типов. Сколько я этого насмотрелся в проектах! Я не понимаю почему все резко стали против библиотек. Я использую ramda, и не вижу ничего плохого. Она полность тришейкабл и может отлично работать с типами.


    1. Dartess
      21.06.2024 09:10

      Помню для тришейкабл нужны были какие-то костыли.

      А вот с типами вообще не соглашусь. Это js-ная либа без типов, типы - сторонние, и у меня регулярно при обновлении зависимостей что-то отваливалось.

      Ещё один минус того что либа js-ная - она обмазана проверками типов. Каждая функция будет проверять, что в неё передаётся, валидировать, приводить типы и стараться покрыть все их комбинации. Это ж сколько лишнего кода!

      В итоге плюнул и пересел на remeda. Стало гораздо лучше. Тришейкается вообще без лишних движений, компактная, изначально ts. Советую посмотреть.


  1. olegkusov
    21.06.2024 09:10

    Вы используете лишь 5 методов.

    А где статистика? Или пост ради хайпа?


  1. KLIJIN
    21.06.2024 09:10

    Может современный мир развращает, но я предпочитаю вместе с chartGPT писать микроутилиты 10-ти строчные и кидать их в папку shared. Так я хоть полностью понимаю и контролирую что происходит в коде.


  1. posledam
    21.06.2024 09:10

    Вопрос автору. А какие минусы в использовании lodash? Понятно, что некоторые функции уже перекрываются нативной поддержкой. Опять же, не все, и не везде. Но вот зачем может понадобиться выпиливание? Что в итоге получим? "более лучший/чистый/правильный код"? Хотелось бы что-нибудь более существенного :)


    1. daniluk4000 Автор
      21.06.2024 09:10
      +1

      Вы простите, конечно, но я процитирую себя:

      В правильный код я не верю. Скорее всего, код без лодаша будет работать быстрее - как минимум после замены на натив. Использование нативных методов также улучшит понимание того, что можно делать в чистом JS. Ну и код, скорее всего, правда будет чище - меньше импортов, меньше неймспейсов с методами.