24 марта 2016 в 16:37

NPM и left-pad: мы разучились программировать? перевод

Автор — Дэвид Хейни (David Haney), ведущий инженер-программист Stack Overflow

Итак, разработчики, время для серьёзного разговора. Вы уже наверное в курсе, что на этой неделе React, Babel и куча других популярных пакетов на NPM сломались. Причина довольно удивительная.

Простой пакет NPM под названием left-pad был установлен как зависимость в React, Babel и других пакетах. Модуль, который на момент написания этого поста, имеет 11 звёзд на Github (сейчас 323 — прим.пер). Весь пакет состоит из 11 простых строчек, которые реализуют примитивную функцию вставки пробелов в левой части строк. Если какие-то из ссылок когда-нибудь умрут, вот его код:

module.exports = leftpad;
function leftpad (str, len, ch) {
  str = String(str);
  var i = -1;
  if (!ch && ch !== 0) ch = ' ';
  len = len - str.length;
  while (++i < len) {
    str = ch + str;
  }
  return str;
}
Что меня беспокоит, так это такое большое количество пакетов, где установлена зависимость от простой функции набивки строки пробелами, вместо того чтобы потратить 2 минуты и написать эту базовую функцию самому.

Узнав о бедствии, которое случилось из-за left-pad, я начал исследовать экосистему NPM. И вот что обнаружил, среди прочего:

  • Есть пакет под названием isArray, который скачивают 880 000 раз в день, 18 млн скачиваний в феврале 2016 года. У него 72 зависимых NPM-пакета. И вот его целая 1 строчка кода:
    return toString.call(arr) == '[object Array]';
  • Есть пакет под названием is-positive-integer (GitHub), который состоит из 4 строчек и которому на вчерашний день требовалось 3 других пакета для работы. Автор с тех пор провёл рефакторинг, так что теперь у пакета 0 зависимостей, но я не могу понять, почему это не было сделано сразу.
  • Свежая установка Babel включает 41 000 файлов
  • Чистый шаблон приложения на базе jspm/npm начинается c 28 000+ файлов

Всё это заставляет задать вопрос…

Мы разучились программировать?


В каком из параллельных миров вышеперечисленные решения являются наилучшими? Как сотни зависимостей и 28 000 файлов для пустого шаблона можно назвать чем-то ещё, кроме чрезмерной сложности и безумия?

Создаётся впечатление, что участники экосистемы NPM взрастили фетиш микропакетов. Вместо того, чтобы написать любую функцию или код, они как будто предпочитают установить зависимость на что-то написанное кем-то другим. Мне кажется, что вся работа программиста в экосистеме NPM сводится к написанию как можно меньшего количества кода, чтобы связать вместе существующие библиотеки, чтобы создать нечто новое, функционирующее уникальным образом для личных или коммерческих нужд.

Функции — это не пакеты


Функции слишком малы, чтобы попасть в пакет и зависимость. У чистых функций нет связи; это случайные фрагменты кода и ничего больше. Кому нужна зависимость от косинуса? Вместо этого нам бы по-настоящему понравилась зависимость от пакета «тригонометрия», который охватывает много хитрых функций, которые мы не хотим писать сами. Это гораздо больше похоже на то, как .NET и другие фреймворки создают базовую библиотеку с основной функциональностью. Такая библиотека проверена создателями языка и обладает в значительной степени гарантированной надёжностью, с отсутствием багов.

Проблема третьих лиц


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

Во-вторых, даже если логика в пакете правильная, меня поражает тот факт, что разработчики устанавливают зависимости на однострочные функции, которые сами должны уметь писать с закрытыми глазами. Если ты не можешь написать функцию left-pad, is-positive-integer или isArray за пять минут (включая время на поиск в Google), то ты вообще не умеешь программировать. Чёрт, любая из них может быть хорошим вопросом на собеседовании как проверка, умеет ли кандидат программировать.

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

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

Боремся за уменьшение зависимостей


Каждый добавленный пакет прибавляет ещё одну зависимость к вашему проекту. Зависимости, по сути этого слова, — то, в чём вы нуждаетесь, чтобы код работал. Чем больше зависимостей, тем больше у вас точек отказа. Не говоря уже о большей вероятности ошибки: вы проверяли кого-нибудь из тех программистов, кто написал эти функции, от которых вы зависите ежедневно?

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

Но пожалуйста, ради любви ко всему, что представляет собой программирование, самостоятельно напишите проклятые базовые функции. Ставить зависимости на однострочные пакеты — это вообще рехнуться. Не верите? Просто спросите у разработчиков React, как у них прошла неделя, и не жалеют ли они о том, что сами не написали те 11 строк для набивки строки слева пробелами.
Автор оригинала: Дэвид Хейни
Анатолий Ализар @alizar
карма
751,5
рейтинг 18,2
Пользователь
Похожие публикации
Самое читаемое Разработка

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

  • –6
    • +4
      Перевод на Хабре
      Small modules: it’s not quite that simple

      Ну и даже в комментариях к самой статье много разумных аргументов.
      • +32
        Ситуацию с npm мне всегда хочется проиллюстрировать этой чудесной фотографией:
        image
        • 0
          А что на этом фото происходит? Хотя бы приблизительно? Кто это? Чем они тут занимаются?
          • +4
            Программируют же! Вы разве не видите ноутбук у самого главного?
          • 0
            Гуглится же: индийский военный парад.
          • 0
            Да, это индийский военный парад, там много разных «фигур высшего пилотажа» было кроме той, что на фотографии. И вишенка на торте: индийцы этот парад еще Обаме с его женой с гордостью демонстрировали, видео гуглится.
            • +2
              Код, фильмы, парады — во всём чувствуется целостный неповторимый стиль)
              • 0
                Ага, древняя культура, религиозный отпечаток, все такое. :)
                • 0
                  Я даже не побоюсь этого слова: «скрепы»!
            • 0
              А ещё есть видео где Обама с какими-то чуваками смотрят парад. Но российский. Так что непонятно, что там на самом деле Обама смотрит
    • +13
      Не первый раз натыкаюсь на эту ссылку и вот очень странное всё-таки впечатление от этого комментария на ГитХаб, sindresorhus пишет:
      For example. I have this module negative-zero. Its job is to tell me if a number is -0. Normally you wouldn't have to care about this, but it could happen. How do you figure out if a number is -0. Well easy x === 0 && 1 / x === -Infinity. Or is it? Do you really want to have to know how and why this works?
      Такой вроде риторический вопрос, мы вроде как должны поникнув головой ответить автору «No, it is not, we don't» и пойти дальше добавлять кучу зависимостей в код для каждого чиха. Но во-первых, на самом деле весь модуль действительно выглядит так:
      'use strict';
      module.exports = function (x) {
      return x === 0 && 1 / x === -Infinity;
      };
      А во-вторых очень странно всё-таки не хотеть хотя бы примерно знать как это должно работать, хотя бы одним глазком взглянуть на исходник, особенно если нам намекают, что там творится какая-то магия! И дальше ещё один неудачный, на мой взгляд, пример:
      Another example. Chalk is one of the most popular modules on npm. What you might not realize is that it's actually a collection of modules. It depends on a module for detecting if the terminal supports color, for getting the ansi escape codes, etc.
      Посмотрите на модули, которые используются в Chalk, про которые пишет автор комментария. Да, с одной стороны они довольно просты, но требуют не самых распространённых знаний ANSI кодов для цвета\стиля или того, как определить их поддержку в терминале. Можно ли это сравнивать с toString.call(arr) == '[object Array]'? Моё мнение, что нет, совсем нельзя. И Девид Хейни в оригинальном посте пишет про то, что для сложных вещей имеет смысл использовать модули, а для однострочных сравнений всё-таки нет
      • –8
        очень странно всё-таки не хотеть хотя бы примерно знать как это должно работать, хотя бы одним глазком взглянуть на исходник, особенно если нам намекают, что там творится какая-то магия!
        Хочется — взгляните, кто ж вам мешает? Мне, например, не хочется. Мне хочется, чтобы работало и бизнесовая логика быстрее писалась.
        • +4
          Никто не мешает, понятное дело. Но, цитируя Девида, "stringing APIs together and calling it programming doesn’t make it programming". Ну и вот всё-таки isArray вам действительно бизнесовую логику позволит быстрее писать? Ну вот прямо буквально, что быстрее:
          сначала в терминал:
          npm install isarray --save
          подождали установку
          потом в ide:
          const isArray = require('isarray');
          теперь наконец можем использовать функцию isArray и в нашем проекте появилось теоретически уязвимое место, ура
          или знаем/загуглили про Array.isArray/[object Array], сразу пишем в коде Array.isArray/[object Array] не тратя время на поиск модуля, установку и объявление? И теперь сборка проекта не сломается, если вдруг этот модуль куда-нибудь денется или автор по ошибке закомитит в него что-нибудь с косяком
          PS: я дико извиняюсь за этот жуткий вид моих комментариев, маркдаун в "предпросмотре" нормально отображался, но для r&c пользователей видимо никакое форматирование текста не доступно
          • 0
            знаем/загуглили про Array.isArray/[object Array], сразу пишем в коде Array.isArray/[object Array]

            А где именно вы это пишете? В отдельном файле utils.js? И в следующем проекте вы тоже будете его копипастить? Это как-то не очень здорово, если честно.
            У меня для таких вещей стоит зависимость от lodash, а у кого-то она будет излишней и зависимость от конкретного isArray будет весьма кстати.
            P. S. Это же Unix-way в чистом виде.
            • +1
              Конкретно у меня все подобные утилиты собраны в один модуль в локальном npm и в проект подтягиваются сразу все, а потом rollupjs при билде убирает лишнее. А isArray там просто нет, потому что всё, подо что мне надо писать код уже давно имеет Array.isArray
              Я понимаю, что "small is beautiful", но должен же быть разумный предел этому, на мой взгляд isArray уже за гранью, на ваш это ок, ну и вот тут мы явно друг друга не переубедим, спасибо за диалог :)
          • +5
            Ок. В одном проекте написали от руки свой привычный однострочник, повторили его в другом, в третьем, в пятом, в десятом. А в одиннадцатом обнаружили вдруг в этом привычном однострочнике баг. Ваши действия?
            • 0
              Ок. Подключили в зависимость однострочник, использовали его в одном, другом, в третьем, в пятом, в десятом. А в одиннадцатом обнаружили что вдруг в этом привычном однострочнике баг, и автор выпустил новую версию. Ваши действия?
              Идти обновлять зависимости сопоставимо с обновлением кода этого однострочника. Если разработчик не понимает что он пишет, то проще нагуглить готовое решение, чем городить велосипед. Такому коду место в gist.github.com, но никак не в пакетах.
              • +1
                Ну так а если в той ОС, на которой все эти проекты крутятся, найдут очередной критический баг, требующия обновления — какие ваши действия? "apt-get update && apt-get dist-upgrade" в случае ОС, "npm install --save" в случае нода (или может svn update, или docker push, или как вы там деплоите). Всяко лучше править Н мест в М проектах.
            • +1
              /весело/
              Создать новый npm пакет (… богу NPM...)
            • +1
              Исправить один раз и дополнить юнит-тесты. Тем более он уже много лет как в utils.js или аналоге, если, конечно, это не первый Hello, World! программиста.
            • +1
              Пишу код, который ищёт все вхождения выражения и заменяет на другое. Многие IDE имееют такую функциональность изкоробки. Пишу жалобный (хабра)пост о мудаках в комитетах стандартизации, которые при переходе на новую версию js поломали обратную совместимость.
              Примерно такой алгоритм действий был при переходе со второго на третий питон.
        • 0
          Проблема в том, что документация к пакету занимает больше места, чем содержимое пакета. Лучше было бы эту строчку разместить в документации, и пусть все желающие утаскивают её к себе в проект.
          • 0
            Да пусть занимает, на что это влияет?
            • 0
              На время, которое потратит программист, использующий этот пакет. Вы же не будете использовать пакет, не прочитав документации?
              • 0
                А что быстрее – прочитать документацию, или написать документацию? Вы же не будете писать свою функцию без документации?
                • +2
                  Буду, конечно. Нет нужды комментировать очевидные вещи.
                  • 0
                    Тогда к чему ваш вопрос про документацию?
                    • 0
                      Чтобы использовать пакет, надо прочитать документацию. Потому что если ты всё знаешь о тонкостях js, ты сам напишешь этот кода, а если не знаешь, то должен откуда-то узнать, что есть сложности. То есть что-то прочитать, скорее всего ту самую документацию.
                      Тогда гораздо проще в документации сразу указать нужный однострочник, чем давать ссылку на установку пакета.
                      • +1
                        Нет нужды комментировать очевидные вещи.
                        Чтобы использовать пакет, надо прочитать документацию

                        Странная логика. Для очевидных вещей документацию писать не надо, но читать надо. Откуда же она будет браться, если её не надо писать?
                        • +2
                          Вот так вот непопишешь документацию или коментарии раз, другой, третий… А через 3-4 месяца смотришь на свой код и думаешь, это вообще кто писал и что он курил? Часто вещи очевидны только в контексте состояния вашего разума (настроения, содержимого кратковременной памяти), а воспринимаются как если бы были очевидными в контексте окружающего кода — страшно обманчивая штука.
                        • 0
                          А что не понятного? Если бы мне была эта вещь очевидной, я бы не использовал пакет, а сразу бы писал однострочник. Но если мне нужно откуда-то однострочник позаимствовать, значит я его не знаю. Значит он не очевиден для меня, и мне нужно его читать.
  • +5
    toString.call(arr) == '[object Array]' — неужели так некрасиво в js определяется тип? Что будет если в будущем поменяется функция преобразования объекта в строку?
    • 0
      В ES5 Array.isArray, поэтому и существуют шимы/полифиллы/мини-модули.
    • +2
      Возможно это просто костыль, ввиду особенностей поведения typeof:
      typeof ''
      «string»
      typeof []
      «object»
      typeof {}
      «object»

      toString.call('')
      "[object String]"
      toString.call([])
      "[object Array]"
      toString.call({})
      "[object Object]"
      • +3
        arr instanceof Array. Не?
        • +1
          Да, и это будет быстрее.
          Call to function isArray1(obj)
          {
          return obj instanceof Array;
          } took 10.195000000000022 milliseconds.

          Call to function isArray2(obj)
          {
          return (typeof obj == «object»);
          } took 12.25 milliseconds.

          Call to function isArray3(obj)
          {
          return Array.isArray(obj);
          } took 26.909999999999968 milliseconds.

          Call to function isArray4(obj)
          {
          return toString.call(obj) == '[object Array]';
          } took 1597.9850000000001 milliseconds.

          Вариант typeof в данном случае реализован не корректно, но тем ни менее.
          • +2
            Функция
            function isArray2(obj) {
            return (typeof obj == «object»);
            }
            бесполезна чуть более чем полностью. isArray2({}) //true
            • 0
              Я об этом дважды написал выше, да. Но спасибо что ещё раз уточнили, во всех тонкостях JS я не силён.
          • +1
            С фреймами не пройдет. Если необходима универсальная проверка то либо toString() либо isArray(). (
            • 0
              Дли тех, кто не понял что имеется в виду или почему не пройдёт (вроде меня), вот в деталях: http://perfectionkills.com/instanceof-considered-harmful-or-how-to-write-a-robust-isarray/
              Там еть и некоторые другие странные способы проверить имеем ли мы дело с массивом или нет.
              Хотя стоит заметить, что лично мне проще проверить существование необходимых мне в последующем коде методов.
              • 0
                После прочтения этого лишь укрепилась вера в убогость языка, где приходится так извращаться ради простых вещей, включая is-positive-integer.
                Даже на php это делается проще и логичнее — if ((int) $value == $value && $value > 0)
                Банально, padding во многих языках решается через sprintf, но его почему-то в javascript нет.
                • +1
                  isArray() работает как надо, не нужно извращаться.
                  is-positive-integer можно также записать в вашем стиле и на js (parseInt(val) === val && val > 0)
                  но это не значит что это единственное и главное всеобъемлющее решение.
                  Безусловно js не лишен проблем, как и любой язык, впринципе, особенно если пытаться использовать его не по назначению.
                  Но убог? Както через чур, мне кажется.
                  • 0
                    var isPosInt = (val) => val + 0 === val && val > 0
                    • 0
                      Чёрт, забыл, что нужно ещё и на int проверить. :)
                      var isPosInt = (val) => Math.trunc(val + 0) === val && val > 0
                      • 0
                        Bitwise приведение к int: var isPosInt = (x) => (~~x) === x && x > 0
                        Есть даже чистый bitwise:
                        http://stackoverflow.com/questions/3912375/check-if-a-number-x-is-positive-x0-by-only-using-bitwise-operators-in-c
                        • 0
                          Чёрт, это же С и тип заранее задан. Там только проверка на positive, без int. У кого-нибудь есть идеи как сделать чистый bitwise is int на JS? -_-
                          • +1
                            Ваш однострочник все больше напоминает какой-то неприличный ASCII-арт.

                            Ну, а по теме:

                            var isPosInt = (x) => (x & -1) === x && x && !(~(-1 >>> 1) & (x & -1));

                            По-человечески:

                            a = (-1 >>> 1) = максимальное положительное целое
                            b = ~a = минимальное отрицательное целое, а на самом деле бит знака = 1, остальные биты
                            c = x & -1 = приводим к целому
                            d = b & c, т.е. берем бит знака из приведенного целого

                            Ну, и все вместе: если приведение к целому строго равно аргументу, и если аргумент не нулевой, и если бит знака аргумента нулевой, то перед нами оно — положительное целое.

                            Или может вы хотите и от && избавиться?
                            • 0
                              И от "&&", и от "===".
                              • 0
                                Кстати, вариант без "&&".
                                var isPosInt = (x) => x === ~~x & !(~(-1 >>> 1) & (x & -1) | !x)
                                • 0
                                  Ах да, можно было вот так: var isPosInt = (x) => x === ~~x & !(~(-1 >>> 1) & ~~x | !x)
                          • 0
                            Не совсем понимаю что вы хотите, сами же пример привели чуть выше. Если целиком у меня вышло както так:
                            var isPosInt = (val) => ~~val === val && !(((val >> 31) << 1) | !val)
                            • 0
                              Чистый bitwise подразумевает полное отсутствие каких-либо иных логических операторов. С === и && я и сам могу. :)
                              Кстати, alexkunin выше дал более коректное решение для проверки на положительное значение. Оно не привязано к разрядности переменной.
                              • 0
                                Ну, по стандарту там 32, т.е. решение немножко избыточно. ;) Но я терпеть не могу такое в константы засовывать.
                                • 0
                                  Ну стандарт-то стандарт, а в реализации могут и начудить. «Волшебные» числа я тоже не люблю.
                        • 0
                          isPosInt(2147483648)
                          false
                          • 0
                            Это число не влезает в 32-битное целое со знаком, т.е. битовые операции с таким уже не работают. Тут нужно решить философский вопрос: нам нужна проверка на целое положительное с точки зрения математики или с точки зрения типа Int32? Наверное, все-таки первое, а значит битовая арифметика не подходит.
                            • +1
                              В JavaScript нет Int32, можно сказать, что есть Int54, и от функции is*Int(eger) ожидаешь проверки именно на Int54. Если ограничиваться Int32, то и именовать нужно соответствующе.
                              • 0
                                На счёт именования согласен. Решение не универсальное и тут уже играет роль важна ли нам скорость или работа с большими числами. Ещё стоит заметить, что числа больше Number.MAX_SAFE_INTEGER не целые по-определению.
                                • 0
                                  Ну так Number.MAX_SAFE_INTEGER и есть "Int54.MaxValue" (в .Net нотации)
                                  • 0
                                    Я, кагбэ, и не говорил, что это не оно. Просто более крупные значения в JS представляется в формате double-precision floating-point и тут уже результат проверки скорее зависит от точки зрения. Данные хранятся в не целочисленном формате, но число получается целое так-как дробная часть с такой точностью не влезает и в этот формат.
                                • 0
                                  Вы таки дайте определение целости. Сейчас вы говорите о том, что числа больше указанного нельзя хранить полностью, т.е. все разряды не влезут в мантиссу. Но можно хранить полностью, т.к. невлезшие разряды все равное нулевые. Например 1e18 пройдет проверку на «целость» в математическом смысле:

                                  1e18 % 1 === 0
                                  (Math.trunc(1e18) — 1e18) === 0

                                  Именно для таких случаев в том модуле — is-positive-integer — есть функция isSafePositiveInteger.
                                  • 0
                                    «невлезшие разряды все равное нулевые» следует читать как «невлезшие разряды у некоторых чисел все равное нулевые».
                                  • 0
                                    Именно это я и имел в виду, когда сказал, что результат зависит от точки зрения. Значение-то целочисленное получается, хоть и хранится в формате для дробных значений и часть данных теряется просто потому, что их негде хранить. И да, я в курсе, что там есть специальная функция для проверки, только именуется она просто isSafeInteger.
                                    Кстати, со строгим ограничением на SafeInteger (и без bitwise, естественно) можно вот так вот:
                                    var isSafePositiveInteger = x => Number.isSafeInteger(x) && x > 0;
                                    • 0
                                      Я говорил о модуле is-positive-integer и его функции isSafePositiveInteger: https://github.com/tjmehta/is-positive-integer/blob/master/index.js, в последних строчках. Интересно, это просто «велик» автора модуля? Или он что-то знает хитрое, и isSafeInteger не подходит?

                                      Кроме этого, похоже, мы полностью сходимся во мнениях.
                                      • 0
                                        Я думаю для понимания автора модуля достаточно взглянуть на это вот:
                                        var MAX_SAFE_INTEGER = Number.MAX_SAFE_INTEGER || 9007199254740991
                                        • 0
                                          Не согласен. Модуль может быть использован и в браузере (есть способы подключения npm-модулей, и еще там всякая экзотика), а вот тут вот есть информация о наличии этой константы в разных браузерах: https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Number/MAX_SAFE_INTEGER. Если кратко, то не работает в IE, в Сафари меньше 9, в мобильных IE, Сафари и Опере.
                                          • 0
                                            А разве у isSafeInteger не та же беда? https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/isSafeInteger
                                            Вот и изобретает велосипеды.
                                            Ну и раз сделал isPositiveInteger, то нужно ж было этот велосипед как-то задействовать? Запихивать Number.isSafeInteger?: и, тем более if в isSafePositiveInteger явно не хотелось, а через || этот isSafeInteger всунуть некуда так-как isPositiveIngeter оно не заменит, а для проверки x <= MAX_SAFE_INTEGER будет избыточным.
                                            Вот в isPositiveInteger и правда можно (и нужно, наверное) было бы попробовать воспользоваться Number.isInteger если есть в наличии.
                                          • 0
                                            Если кратко, то это ES6 :)
                              • 0
                                Так же, как «можно сказать есть Int54», есть и Int32/Uint32, и даже Int16/Uint16. Некоторые операторы (в основном битовые) работают именно с 32-битными целыми (как со знаком, так и без). Их легко найти в стандарте, поискав ToInt32 и ToUint32.

                                На счет ожидания исходя из имени, тут неразбериха случилась. Тема обсуждения положительных целых началась с упоминания модуля is-positive-integer. Из названия и содержимого модуля видно, что имеется в виду именно математическое понятие «целое число».

                                В ходе обсуждения isPositiveInteger сократили до isPosInt, и я уверен, что подменять понятия — математическое «целое число» на компьютерный «целочисленный тип, вмещающийся в конечное число бит» — никто не собирался. Просто лень набирать такой длинный идентификатор.

                                Так что, исходя из названия, не стоит ожидать как Int54, так и Int32. Хотя конкретно в последних комментариях обсуждался способ исключительно на битовых операциях, что автоматически ограничивает область действия 32-битными числами.
          • 0
            {
            
            const src = [1,2,3,4,5];
            const wrong = { foo: 1 };
            
            const arr1 = it => typeof it[Symbol.iterator] === 'function';
            const arr2 = it => it instanceof Array;
            const arr3 = it => Array.isArray(it);
            const arr4 = it => toString.call(it) == '[object Array]';
            
            Array.prototype.forEach.call([ arr1, arr2, arr3, arr4 ], (f, i) => {
              const fname = `arr${i+1}`;
              console.log(`----- ${fname} -----`);
              console.time(fname);
              const result = f(src);
              console.timeEnd(fname);
              console.assert(result === true);
              console.assert(f(wrong) === false);
            });
            
            }
            /*
            ----- arr1 -----
            arr1: timer started
            arr1: 0.63ms
            ----- arr2 -----
            arr2: timer started
            arr2: 0.55ms
            ----- arr3 -----
            arr3: timer started
            arr3: 0.53ms
            ----- arr4 -----
            arr4: timer started
            arr4: 1.2ms
            */
          • +1
            Извините за глупый вопрос, но не могу не переспросить:
            Определение типа таким способом "toString.call(obj) == '[object Array]';" занимает больше 1.5 секунд?
            • +1
              Выполнение таким способом проверки сто тысяч раз для обычного массива, ассоциативного массива и строки займёт полторы секунды на моём ноутбуке в хроме какой то версии близкой к последней стабильной.
        • +2
          Этот вариант не сработает, если массив был создан в другой глобальной области видимости.
        • –1
          typeof obj[Symbol.iterator] === 'function'
          • 0
            Для строки так же вернёт true, но можно объединить с typeof obj == "object".
        • 0
          это работает не всегда — если, скажем, массив создан в web-worker'e, то это дело вернёт false.
        • 0
          Сколь я помню, это не гарантирует истинности. Если arr пришел из другого окна например, то будет false.
    • +2
      Типы — это давняя боль. В первой версии языка вообще не было массивов (поэтому arguments — не массив), их эмулировали с помощью объектов с ключами 0..n. Отсюда все эти ноги растут. А еще вот такое счастье: typeof null === 'object'.
      Ну а вообще в 2016 есть Array.isArray. В крайнем случае, для легаси окружений, можно и полифилл поставить.
    • –4
      А если так?
      return !![].forEach(или любая другая функция, имеющаяся только у массива)
      • +1
        Это очень странная и неочевидная конструкция, которая сильно ухудшает читаемость кода. Поэтому такие штуки выносят в или библиотеки или отдельные пакеты.
    • –2
      (нервно) хахаха. Как вы далеки от этого криволикого языка...
  • –2
    Ну, прям крайность GNU ) Вырожденная модульность, чтобы в любой момент пользователь мог заменить зависимую библиотеку на более любимую)
  • +20
    Ну а вообще проблема в бедной стандартной библиотеке. Вместо того, чтобы починить, например, Date и принять String#padLeft и прочее, что можно легко заполифиллить и снять эти идиотские проблемы, TC39 занимается… чем-то другим.
    • +1
      Им уже предлогают String.prototype.padStart и String.prototype.padEnd: github.com/tc39/proposal-string-pad-start-end
      • +4
        Предложено было уже давно, в ES2015 и 16 не попало. Поэтому я и сказал, что занимаются невесть чем вместо вещей которые нужны каждый день.
  • +5
    Мне кажется, что подобное(втягивание пустяковых зависимостей) происходит в первую очередь из-за того, что кодерам не до перфекционизма. Цель — выкатить решение.
    • НЛО прилетело и опубликовало эту надпись здесь
      • +5
        Так это ж три этапа развития разработчика (на правах полу-шутки):
        1) Копипастишь.
        2) Не копипастишь, а переиспользуешь.
        3) Понимаешь, когда копипастить можно, а когда не нужно.
        Много разработчиков остаются на втором уровне понимания (отчасти, я думаю, из-за перфекционизма, типа модули, переиспользование кода, красота же).
    • +1
      Именно, когда (возможно этого и не случится) вдруг бизнес повернет на курс создания надёжных, продуманных продуктов то ситуация сама собой исправится. Сейчас все гоянятся как на скачках не понятно куда.
  • 0
    Чистить зависимости вручную, то еще удовольствие, особенно когда их набирается с десяток. Выход, нужен инструмент для выявления неиспользуемых зависимостей, иначе эта куча будет только расти.
    • +1
      https://github.com/dylang/npm-check Весьма неплохо справляется.
      Есть еще https://github.com/depcheck/depcheck но мне лично он нравится меньше.
      • 0
        1. Спасибо за ссылку, обязательно проверю.
        2. Ну тогда осталось только чтобы npm запускал проверку перед публикацией и не разрешал публиковать "загрязненные" пакеты
  • +5
    Нет ничего плохого в маленьких модулях, в модулях из 1 строки и даже из 0 строк (модуль без кода, представляет собой коллекцию полезных зависимостей, например). Это очень хорошо, что люди используют проверенные решения даже для примитивных задач, потому как есть немало шансов, что даже модуль из трёх строк спасет программистов от багов, возможныз при самописной реализации той же проверки на массив, встречающихся в очень редких ситуациях, т.к. "у меня же работает!".
    Плохо в npm и во всей этой экосистеме с фреймворками друг на друге это их огромнейшая раздутость, 28000 файлов, вот это вот всё. Реально нужный и используемый код из всего этого — это доли процентов чаще всего. Но кому до этого есть дело, в смартфонах уже по 8 ядер, анимация почти не тормозит!)
    • +17
      >люди используют проверенные решения даже для примитивных задач

      к сожалению, большинство npm-модулей отнюдь не проверенные решения.
      • +12
        Именно так. Приведённая функция leftpad крайне неоптимальна: она имеет сложность O(N^2). Но на это обратили внимание только сейчас, когда поднялась шумиха:
        github.com/azer/left-pad/issues/15
        То есть автор написал функцию для своих нужд. Возможно, ему требовалось быстрое и надёжное решение для строк небольшой длины, а оптимизацию для длинных строк он решил оставить на потом, о чём благополучно забыл. А другие взяли и стали его функцию использовать, не особо вникая в то, как эта функция работает.
        • 0
          И это аргумент, почему лучше использовать готовый модуль, а не самописную функцию (или, вообще копипаст).
          Вот смотрите:
          1. Народ обратил внимание на то, что сложность O(N^2).
          2. Грамотный чувак сделал из неё оптимизированную O(N) и бросил пулл-реквест.
          3. Автор принял пулл-реквест и обновил репозиторий.
          4. С этого момента миллионы чуваков делают npm update (или как там) и у них код начинает работать быстрее и эффективнее, без усилий и рефакторинга с их стороны. И пролетают те, кто вместо использования готового лепит велосипеды и забивает на них. Тут можно возразить, что без тестов может что-то поломаться. Ну те, кто не пишут тесты на свой код — ССЗБ.

          Вся мощь модулей в том, что вы можете отдать части логики на опенсорс-аутсорс, где она дотачивается и оптимизируется. И иметь возможность быстро обновиться до актуальных версий. Чего не скажешь о самопале, глубоко зарытом в коде проекта. Даже маленьких модулей, которые вынуждены покрывать крайнюю нищету и скудость стандартной библиотеки. Непонятно, чем занимаются авторы ES-стандартов, если такие нужные каждый день в быту однострочные вещи приходится подтягивать из npm-репы.
          • +8
            Народ обратил внимание на это только после привлечения внимания к этому модулю. Из-за чего у меня сразу возникают вопросы:
            1. Что мешало грамотному чуваку это сделать на год-два раньше?
            2. Почему все бездумно используют модули, но не смотрят, что у них находится под капотом?
            3. Почему принято считать, что велосипед всегда будет хуже, чем ранее написанный код? Кстати, в велосипеде проблемы нет: ведь тесты-то уже написаны!
            4. Что делать, если автор умер и не больше не поддерживает свои библиотеки?
            • 0
              Ну а к самописной функции вообще никогда внимание бы не обратилось.
              1. ровно то же, что может помешать разработчику велосипеда увидеть более оптимальное решение
              2. потому что, по-идее, для обычного использования достаточно смотреть документацию. Вот когда вопрос стоит в оптимизации/дебаггинге – тогда да, без ковыряния в коде не обойтись (если затык в конкретном модуле).
              3. потому что "ранее написанный код" уже работает и так или иначе проверен, а велосипед – нет. Вместо написания велосипеда лучше обновить "ранее написанный код" (пулл-реквесты и т.д.)
              4. форк сделать, например. В чём проблема?
              • +2
                Есть люди, которые занимаются написанием, вылизыванием и поддержкой стандартных библиотек. Это их основная задача. Их код можно и нужно использовать.
                А когда дело касается модулей, которые представляют из себя точно такие же самописные велосипеды, только выложенные в репозиторий, в качестве кода возникают сомнения. Но всегда находятся программисты, которые считают, что пусть и плохой, но выложенный в репозиторий код — это благо.
                1. Автор модуля пулл-реквест может не принять или принять не сразу, а изменения нужно внести прямо сейчас, что делать?
                2. Проблема в том, что миллионам разработчиков придётся переправлять зависимости на новый форк.
            • 0
              Что мешало грамотному чуваку это сделать на год-два раньше?
              А зачем?
              Почему все бездумно используют модули, но не смотрят, что у них находится под капотом?
              Потому что люди так устроены. Они не роботы и не хотят делать то, чего можно не делать.
              Почему принято считать, что велосипед всегда будет хуже, чем ранее написанный код?
              Потому что велосипед нужно написать, очевидно же.
          • +9
            Суть в том, что пункт 1 произошел, в общем-то, случайно. У сотен и тысяч подобных модулей (а у более сложных — так тем более) это не случится никогда.
            • 0
              Неправда. Если это действительно такое тормозное и отъедающее много времени место, то первый же сеанс профилирования покажет, что именно в этом месте алгоритм надо оптимизировать, и при достаточной чистоплотности — выложить в open source форк/pull request.
              • 0
                "Привет, я Вася Пупкин, и я запускаю профайлер два раза в год." (с) Общество анонимных профайлеро-незапускателей. Серьезно, кстати, бывает заказчику говоришь — тормозит, надо профильнуть и переписать чуток! А он такой — да не парься, арендуй сервак побольше, вот тебе логин на амазоне.
                • 0
                  Обычно да, так всё и происходит. :) Неоптимальный код заливают баблом на железо. Но это бывате только там, где что железо дешевле времени разработчика. Если было бы наоборот — сидели бы все безвылазно в профайлерах. :)

                  Ну и если совсем уж всё плохо и пара часов времени разработчика дешевле двух сотен серваков для хайлоад-проекта — то запустят-таки и исправят. Вон, фейсбук и вконтакт дальше пошли — то компилируемый PHP изобретут, то из этого PHP всё тормозящее ООП повыкидывают, то на C++ с ассемблерными вставками перепишут критичные сервисы.
                  • 0
                    Ну, все праильно, но на крупные проекты с высокой нагрузкой и вменяемыми менеджерами работают жалкие проценты всего контингента девелоперов. Не удивлюсь, если включение нового стороннего модуля проходит ревизию у сениора или специального супервайзера. В основном-то народ мелкую лабуду делает. И вот еще, встречал проекты, которые работают там, где крутятся миллионы (в смысле не фри-ту-плей, где в месяц на килобакс движения — уже много, а какие-то инвестиционные инструменты), а при этом тестов нету. Ни одного. И как тут профилировать и что-то менять? Странных людей много...
              • 0
                Конкретный практический пример показал, что не случился ни у кого "первый же сеанс профилирования". А если и случился, то скорее всего, никто не стал заморачиватся доработками и пулл-реквестами, а просто нашел что-то другое.
        • 0
          Да, но бонус в том, что если в этой библиотеке починят и оптимизируют — вы тоже будете в выигрыше, если обновитесь.
        • –3
          На самом деле, там O(N) и предложено решение O(log(N)).
    • +3
      > потому как есть немало шансов, что даже модуль из трёх строк спасет программистов от багов,
      > возможныз при самописной реализации той же проверки на массив,
      Точно так же есть немало шансов (мне сдаётся, что их там даже больше), что найденный по потребности и добавленный в зависимости модуль — это нифига не проверенное решение, и имеет свои подводные камни, фичи и баги. Как по мне, если кусок кода тривиален и прост, его нужно иметь в своём решении, а не тянуть как зависимость. По крайней мере, скопировать, а не переписать с нуля, раз уж он такой хороший.
      • –2
        То, что используют миллион программистов всяко более оттестированно, чем то, что вы напишете сами.
        • +1
          … и любая проблема, сознательно или несознательно вносимая автором, сразу ломает миллион проектов.
          • –1
            Зафиксируйте версию и обновляйтесь только по мере надобности. Зависимости вида * или 1.* или ^1.2 — это все дурной тон.
        • +3
          Я бы не был настолько уверен насчёт качества тестов. Программист написал какие-то тесты и забыл. А миллионы программистов смотрят на модуль: код есть, тесты есть, можно пользоваться.
          • 0
            Если их миллион — раз не жалуются и не заводят баги, то велика вероятность, что все окей. Миллионы мух не могут ошибаться ;)
  • +7
    Напомню про платную программу под ms-dos, состоявшую из 0 байт и позволявшую быстро запустить предыдущую запущенную программу (трюк состоял в том, что вызывался предыдущий загруженный в память код).
    Вопрос не в размере кода, а его продуманности и удобстве. Переиспользование кода — благо.
    • +10
      Уточнение интересующимся про программу: https://habrahabr.ru/post/147075/
      • 0
        Да, спасибо, не мог найти.
    • +2
      Переиспользование кода — благо

      С некоторыми уточнениями:
      1. В общий код легко добавлять новые вещи, очень тяжело менять существующие и совсем тяжело, что то удалять. Что бы обеспечивать обратную совместимость, этот код обычно замусоривается.
      2. Любое изменение в коде, даже баг фикс требует регрессионное тестирование в коде, который переиспользует этот общий код. Почему? потому что даже если это баг, возможно, что бы его обойти, пристроили костыль и теперь, без бага, этот код не будет работать корректно.
        Конкретный пример из npm, есть проект который использует angular2 и для него нужно поставить еще несколько пакетов, у всех пакетов стояла версия с префиксом ^ (версия такая то или совместимая) после npm update апликация перестала работать, выдавая странные ошибки, пришлось вручную перебирать версии, пока не нашли работающую комбинацию.
    • +1
      "переиспользование кода" должно иметь разумную границу.
      Не вчитывался в каждый комментарий тут, но вроде только в одной ветви люди задались вопросом — почему такие элементарные и настолько массово используемые вещи, не внесены в стандартную библиотеку до сих пор?
      • 0
        Стандартная библиотека в данном случае — тянущаяся с собой каждый раз или встроенная в браузер?
        Если первое — тут я тоже не понимаю, что мешает.
        Если второе — ну, у каждого разработчика браузеров будет своё видение "стандартной библиотеки", пока они согласуют свои представления, пока заимплементят, пока вся юзер-база перейдёт на версии, которые имеют в себе эту самую библиотеку...
        • +1
          "с собой или в браузере" — не знаю. Не настолько глубоко погружен в JS, что бы даже версию выдвинуть.
          Но очевидно, что наиболее популярные вещи не должны оставаться "независимым пакетом", и кто-то, кто близок к разработке стандарта языка, должен следить и за содержимым стандартной библиотеки, втягивать их "внутрь", покрывать тестами, оптимизировать и так далее. От этого прямо зависит популярность и удобство инструмента.
          Что там у нас сейчас самое популярное — java? Сколько сил вложено в стандартную библиотеку и в наиболее популярные библиотеки/фреймворки? Следующее C++? Тоже не нужно искать примеры, насколько лучше, когда язык и стандартная библиотека идут за потребностями разработчиков.
          • +1
            Но очевидно, что наиболее популярные вещи не должны оставаться «независимым пакетом», и кто-то, кто близок к разработке стандарта языка, должен следить и за содержимым стандартной библиотеки, втягивать их «внутрь», покрывать тестами, оптимизировать и так далее. От этого прямо зависит популярность и удобство инструмента.
            Они как раз начали этим заниматься и теперь довольно плодотворны на обновления языка.

            Что там у нас сейчас самое популярное — java?
            JavaScript
  • +11
    Фам Нювен несколько лет провел, обучаясь программировать и исследовать. Программирование восходило к началу времен. Как та навозная куча за замком отца. Когда ее промыло ручьем на десять метров в глубь, обнаружились искореженные корпуса машин – летающих машин, как говорили крестьяне, еще от тех великих дней колонизации Канберры. Но та навозная куча была чистой и свежей по сравнению с тем, что лежало в локальной сети «Репризы». Были программы, написанные пять тысяч лет назад, когда человечество еще не покинуло Землю. И самое чудесное (самое ужасное, как говорила Сура) было то, что, в отличие от бесполезных обломков прошлого Канберры, эти программы все еще работали! И через миллион миллионов запутанных нитей наследования многие из старейших программ все еще выполнялись во внутренностях системы Кенг Хо. Например, методы слежения за временем у торговцев. Поправки вносились неимоверно сложно – но на самом дне лежала крошечная программа, которая гоняла счетчик. Секунду за секундой отсчитывала система Кенг Хо с того момента, как нога человек ступила на Луну Старой Земли. Но если приглядеться еще пристальнее… начальный момент был миллионов на сотню секунд позже; момент «ноль» одной из первых компьютерных операционных систем Человечества.

    // Виндж, «Глубина в небе».
  • НЛО прилетело и опубликовало эту надпись здесь
  • +9
    >> мы разучились программировать?
    >> return toString.call(arr) == '[object Array]';
    Скорее, не научились программировать на JS. Конкретный пример — исключительно фактологическое знание, не фундаментальное.
  • +13
    Напоминает http://youmightnotneedjquery.com/, где предлагают заменить
    $.getJSON('/my/url', function(data) {});

    на
    var request = new XMLHttpRequest();
    request.open('GET', '/my/url', true);
    
    request.onload = function() {
      if (request.status >= 200 && request.status < 400) {
        // Success!
        var data = JSON.parse(request.responseText);
      } else {
        // We reached our target server, but it returned an error
    
      }
    };
    
    request.onerror = function() {
      // There was a connection error of some sort
    };
    
    request.send();

    или
    
    $(el).fadeIn();

    на
    function fadeIn(el) {
      el.style.opacity = 0;
    
      var last = +new Date();
      var tick = function() {
        el.style.opacity = +el.style.opacity + (new Date() - last) / 400;
        last = +new Date();
    
        if (+el.style.opacity < 1) {
          (window.requestAnimationFrame && requestAnimationFrame(tick)) || setTimeout(tick, 16);
        }
      };
    
      tick();
    }
    
    fadeIn(el);

    То бишь я сажусь писать новый проект и пишу 10000 строк кода с хелперами потому что умею прогать, зашибись.
    • +1
      Думается приведенные в статье примеры скорей нужно если и вендорить то путем копирования кода под реп.
    • +2
      Вот конкретно данный функционал нет смысла писать самому, так как эти функции не являеются отдельными зависимостями, но частью одного целого.
      • +1
        Т.е. если на все приложение нужно сделать в 3 местах запросы на получение JSON, то правильнее будет загружать jQuery?
        • +4
          Ну, если ваше время и время тех, кто это будет потом поддерживать, бесконечно, то — нет, не правильнее.
          Если это одноразовое приложение, у которого не будет поддержки по каким-либо причинам, то тоже — нет, не правильнее.
          В остальных случаях — да, правильнее загрузить jQuery.
          • +6
            В любом случае с точки зрения надежности будет правильнее загрузить jQuery. Количество человеко-часов, в сумме затраченных на написание всей ajax-части jQuery, огромно. Там учтены случаи совместимости, которые вы в своей практике не встретите в силу закона больших чисел (конкретно такая вот комбинация версий дополнительных библиотек, браузера и, скажем, разрешения экрана приводит к такому-то глюку, и 100 млн пользователей jQuery нашли этот глюк, и он уже пофиксен).

            Но тащить лишние десятки килобайт тоже не комильфотно, так что вам придется сделать выбор: хотите надежности, проверенности — используйте (сюрприз!) проверенную библиотеку; начхать на надежность, наличие редких багов не критично, важен размер — вставьте первый попавшийся велик со стак-оверфлоу.
            • +1
              А где граница между чужим проверенным кодом, который точно будет лучше (быстрее и/или надежнее) чем свой, и кодом непонятно откуда взятых NPM модулей с зависимостями от других непонятных модулей?

              Она ведь у каждого своя? И основана на собственном опыте/умениях/целях?

              По ссылке выше прямо в адресе сказано «вы _можете_ обойтись без». :)
              • +1
                Граница в таких делах всегда нечеткая, тут нет простого алгоритма, придется использовать свои когнитивные способности, опыт и интуицию. Я вот раньше считал, что если автор библиотеки — гугл (мс, оракл, ...), то все будет зашибись. Ан нет. Кстати, особенно хорош чужой провереный код, который вы сами его проверили. У меня нет сомнений, что любой комментирующий в этом топике может написать свой $.ajax. Но есть сомнения, что: 1) каждая реализация с первой попытки будет правильной 2) будет правильной для распространенных браузеров 3) будет правильной для нераспространенных браузеров 4) не будет упрощением для конкретного случая 5) будет правильно и удобно отрабатывать типичные майм-типы вроде app/json 6) не засорит глобальный неймспейм 7) будет поддерживаться автором 8) будет обкатана на миллионах страниц и т.д. и т.п. Но мысль ваша понятна: для проектов уровня jQuery аргументы вроде проверенности убедительны, а для left-pad — не очень. Вот тут и нужно применять свои когнитивные к каждому конкретному случаю. Подбирать каждую зависимость как партнера на следующе 50 лет жизни, а может хватать что плохо лежит для быстрого прототипирования — смотря какие ваши цели.
            • 0
              А не лучше тогда уже 1 раз написать код, который будет выдёргивать из необходимых вам либ реально используемый вами код и выгружать это всё дело в какой-нибудь deps.min.js? Многие и так держат у себя локальную копию того же jQuery, так что следить за обовлениями локальных копий не должно составить труда.
              • 0
                Вы представляете себе сложность выдергивания функций и их зависимостей из исходников, написанных на динамическом языке? И как отдельный случай, выдергивание, скажем, фабричного метода, порождающего целое семейство виджетов, хотя вам нужны только 2 из 20.

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

                Кстати, многие большие библиотеки собираются из довольно мелких модулей. Вы можете не тащить тот же весь jQuery или jQueryUI в виде большого файла, а подключить все дерево исходников и вписать только нужные компоненты в билд-процесс вашего проекта.
                • 0
                  Догадываюсь, видимо потому живых примеров такого ещё ни разу не видел. Хотя мне это кажется вполне реалистичным (если все забудут о существовании "eval").
                  А вот дёргать отдельные модули идея действительно здравая.
                  • 0
                    На счет модулей, jQueryUI даже кажется имеет билдер-страницу с галками: выбрал компоненты, получил пресонализированный билд. Удобно.

                    Кроме eval, полно еще всякого. Например jQuery[location.hash.substr(1)](a, b, c) — и вот что тут скажешь? Немного утрировано, но верно.
              • 0
                https://developers.google.com/closure/compiler/ — уже проделано до нас. Скажем спасибо корпорации зла.
                • 0
                  Кстати да, совсем забыл, отличная штука. Немного нервов дает advanced mode, когда его натравливаешь на чужие пару мегабайт кода.

                  Вот что он сделал с примером, который я выше привел. Вход:

                  var test = {
                  here: function (a, b, c) { console.log(«here», a, b, c); },
                  there: function (a, b, c) { console.log(«there», a, b, c); },
                  everywhere: function (a, b, c) { console.log(«everywhere», a, b, c); }
                  };
                  var a = 'a', b = 'b', c = 'c';
                  test[location.hash.substr(1)](a, b, c);

                  Выход:

                  ({b:function(a, b, c) {
                  console.log(«here», a, b, c);
                  }, c:function(a, b, c) {
                  console.log(«there», a, b, c);
                  }, a:function(a, b, c) {
                  console.log(«everywhere», a, b, c);
                  }})[location.hash.substr(1)](«a», «b», «c»);

                  Сама оптимизация переменных и т.п. прекрасна. Но вот код теперь не работает, потому что ключи переименованы, хотя их значения явно сравниваются с данными, приходящими извне.

                  Да, такие места можно отмаркировать, мол «тут так надо, не поломай». Но я не буду каждый модуль из bower лично расписывать. В общем, плохо.

                  Кстати, я еще попробовал вариант, в котором «var test» заменил на «exports.test», но ключи были все равно переименованы. Хотя вроде бы очевидно, что тут можно делать, а что — нельзя.
                  • 0
                    Разумеется, в документации указаны требования к стилю кодинга, соблюдение которого гарантирует безопасность преобразований. ИЧСХ, эти требования очень часто соблюдаются, если js код генерирует не человек, а транслятор из программы, написанной на другом языке программирования.
                    • 0
                      Т.е. этот инструмент можно использовать только с библиотеками, авторы которых специально нацеливались на совместимость ним.

                      В случае jQuery не только сама библиотека должна быть специальным образом написана (а она так НЕ написана), но и ваш код. К примеру вместо такого:

                      $(elem).width()

                      придется писать такое:

                      $(elem)['width']()

                      Из-за таких вот дел Closure Compiler практически невозможно использовать, разве что у вас весь код написан вами и с самого начала в специальном стиле, или может быть у вас есть время переделать все внешние библиотеки (и делать это для каждой версии).
                  • 0
                    Доброго времени суток
                    Использую Closure Compiler как корректор, иногда полезно :)

                    А корекцию вашего примера я понимаю так.
                    Переменные локальны и в именах нет особой необходимости он и сделал их анонимными.

                    Если хотите оставить имена используйте, --compilation_level=SIMPLE, правда и коррекция будет послабей.

                    Имена переменным, еще до Closure, давал a, b и т.д.
                    • слишком много времени уходит, на подбор лаконичного имени,
                    • после оптимизации или тестирования, часть их вовсе не нужны.
                    • 0
                      Так вот именно: компилятор ключи массива (даже не переменные) записал в «ничего важного», хотя массив индексируется строкой, приходящие извне (хэш текущего адреса). И даже после явного выноса переменной наружу («exports.test» вместо «var test») ключи все равно изменялись.

                      На счет имен переменных, я (и любой code style guide) с вами категорически не согласен. Можно спорить о способе записи имени (через подчеркивание, через кэмел-кейз), о включении типа данных (bIsFirst, oWindow), но однобуквенные переменные, не являющиеся элементами цикла, вам не удастся протащить через линтер. Дело ваше личное, конечно. Ну, до тех пор, пока оно ваше и личное.
                      • 0
                        Содержимое
                        не являющиеся элементами цикла
                        — пример можно о таком не слышал
                        Не массив, объект

                        • 0
                          Имелась в виду переменная-счетчик в цикле: for (i = 0; i < 10; i++) {… }. Тут все исторически сложилось, причем есть три таких имени для трех уровней вложенности: i, j, k.
                      • 0
                        Содержимое
                        Так это, привычка?
                        От вредных привычек можно избавляться, если счетчик задается задолго до цикла например: параметром функции, или в другом цикле?
                        • 0
                          Счетчик задается *параметром функции*? Или в *другом цикле*? Слушайте, простите меня, пожалуйста, но тут я закончу нашу дискуссию. Иначе мы просто повторим комментарии к вашим публикациям с тем же нулевым результатом.
                          • 0
                            Содержимое
                            Простите но вы не ответили, на вопрос.
                            но однобуквенные переменные, не являющиеся элементами цикла, вам не удастся протащить через линтер

                            — пример можно о таком не слышал

                            Иначе мы просто повторим комментарии к вашим публикациям с тем же нулевым результатом.

                            Я думал мы обсуждаем, Google® Closure Compiler, а не переходим на личности, или у Вас есть аргументы по поводу
                            с тем же нулевым результатом

                            Тогда лучше в приват.
                            • 0
                              На вопрос я ответил: элемент цикла — переменная-счетчик, общепринято использовать i, j, k.

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

                              Еще раз, кратенько. Наши с вами представления о коде настолько различаются, что любое обсуждение рискует завязнуть в дискуссиях о самых базовых вещах.
                      • 0
                        Содержимое
                        Вот эти вот «параметром функции» и «в другом цикле» — это признаки «кода с душком».

                        — Если функция, должна выполниться для элементов с [параметр] и до последнего элемента.
                        — Если в цикле обрабатываю строку но хочу пропустить пару символов, которые определил в родительском, цикле.
                        Ну и где здесь душок?
                        ваши представления о правильном и хорошем коде очень сильно различаются с общепринятыми

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

                        Грубить даже не пытался, поправил, насчет массива? Но и я мог описаться и не раз.

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

                        Думаю, что ответил на ваш вопрос о Closure.

                        • 0
                          Как я уже сказал, наши представления о коде различаются с самых основ. Разводить флейм я не хочу, поэтому прекращаю разговор. Если вы считаете, что я просто неправ — отлично. Если у вас есть какое-то ощущение, что в чем-то я может и прав, или что могу что-либо полезное подсказать (хотя откуда, моего кода вы не видели, только комментарии, которые уже считаете неверными, а вот я ваш код уже видел и сделал выводы) — прошу в личку, но опять же, я не буду с вами спорить, а просто минимально аргументированно отвечу на конкретные вопросы.
                          • 0
                            Содержимое
                            Спасибо, за предложение, обращусь.
                            Насчет моего мнения о ваших комментариях, это вы зря, как еще проще узнать, если не спросить, я и сам такой и за приглашение в личку спасибо, но уже поздно, хотя я и на работе.
                            Спасибо, было приятно пообщаться.
                • 0
                  А я-то гадал чем они генерируют свои адовы скрипты для GMail и всего остального.
    • 0
      Тссс, строго между нами
      $.getJSON('/my/url', function(data) {});
      уже можно заменить на
      fetch('/my/url').then(function (response) {});
      • 0
        fetch ещё не вошёл ни в один стандарт ecmascript
        • –1
          И не войдёт, он часть стандарта HTML
          https://fetch.spec.whatwg.org/
          • 0
            Хм, стандарт html это в смысле стандарт DOM API?

            И может это для кого-то элементарно, но всё же: по ссылке https://fetch.spec.whatwg.org указано что это «living standard».
            Что это вообще значит? Означает ли это что стандарт ещё не стабильный в будущем в нём могут произойти изменения ломающий обратную совместимость?
            • 0
            • 0
              Это значит что WhatWG — это не W3C и они немного иначе относятся к тому что есть стандарт.
      • +1
        Согласно MDN IE и Safari пока не поддерживают
        Скрин оттуда
        IE и Safari пока не поддерживают fetch
      • +1
        Тссс, ещё нельзя. Ах да, в полифил нельзя, иначе ты "типа разучился программировать и лах" ©.
  • +3
    Вполне закономерная ситуация, и проблеме не в npm, сотнях однострочных модулях и зависимостях, а в том, что у js очень бедная стандартная библиотека, люди начали решать ее отсутствие такими вот костылями чтобы не писать в каждом проекте isArray вручную, в конечном счете как это обычно и бывает, костыль поднялся и ударил людей по лбу.
  • +2
    Мне кажется, что вся работа программиста в экосистеме NPM сводится к написанию как можно меньшего количества кода, чтобы связать вместе существующие библиотеки, чтобы создать нечто новое, функционирующее уникальным образом для личных или коммерческих нужд.
    А разве это не цель, для которой пишутся программы? Не Ваша цель, как программиста, а цель написания программы?
    • +1
      Честно говоря, я именно так и пишу программы. И пару лет назад писал о том, что современному программисту для выполнения типичных задач достаточно взять какой-нибудь фреймворк и связать несколько вещей между собой, за что меня побили.
      • 0
        Ну, а нетипичные задачи на 99% состоят из типичных подзадач, которые уже кто-то решал и поделился результатами. Часто ли приходится программировать такие задачи, которые еще никому не встречались? На заре отрасли — постоянно, сейчас — редчайший случай.
  • 0
    Проблема описываемая автором обратная сторона велосепедо строения.
  • 0
    Мы не разучились программировать, просто сейчас программирование выглядит именно так, что ты должен выпустить продукт как можно быстрее. Поэтому моя работа скатилась из программиста в собирателя модулей, это нормально сейчас на js проектах, устанавливаешь bower/npm пакет, и просто вставляешь его, своего кода в проекте 20%-30% и то половина этого это в целом работа с пакетами. В целом я уже забыл когда я хотя бы часов 5 программировал, а не имплементил пакеты. Что грусно.
  • +1
    Ситуацию бы исправили более-менее вменяемые библиотеки наиболее востребованных функций. Чтото вроде Glibc (из linux) или stdlib…
    • 0
      Они есть, но никому не нужны, т.к. всем важен размер яваскрипта. Сильно влияет на юзабилити сайта.
      Что действительно нужно яваскрипту — так это оптимизирующий минификатор, способный выкидывать неиспользующийся код. Вопрос в том, как это сделать в таком языке, как яваскрипт...
      • 0
        Внешняя утилита? Или как часть статического анализатора кода (Который тож будет не лишним).
      • 0
        Как-то компилятор/линковщик умудряется вставлять в окончательный бинарник прошивки для микроконтроллера не вообще всю библиотеку, а только реально используемые функции. Это что, принципиально невозможно в JS?
        • +3
          Малость мешает отсутствие статической строгой типизации. Никогда нельзя точно сказать, что реально используется, а что нет.
          • 0
            Именно! Приведу очень простой пример:
            if (false){
            var x = 0;
            }

            «x» in window; // true
            Вот, есть ветка которая вообще никогда не выполнится, так как проверяется литеральная константа. Если мы её выпилим, то получим в выражении снизу уже не «true», а «false»!
            • +1
              Это неправильный пример, все var переносятся в начало функции независимо от места объявления.
               
              Главные враги статического анализа — eval и конструкции вроде
              library['fun'+'ction']()
              

      • 0
        C ES-модулями это возможно. Погуглите rollup и tree-shaking.
    • +1
      Уже есть Underscore и Lodash
  • +7
    Мне кажется, проблема с маленькими модулями в том, что никогда не знаешь, как они поведут себя друг с другом и в каком стиле они вообще сделаны. Что будет, если вместо входной строки подать undefined? Или null? Будет ли исключение или молча превратится в "undefined"/"null"? Вы взяли left-pad и привыкли к одному поведению. Завтра вам понадобится right-pad, а его написал другой человек, у которого другие представления о граничных случаях. И ещё окажется, что порядок аргументов другой. Другое дело, если есть какой-нибудь string-utils, где сто функций с общим стилем, где всегда знаешь, как библиотека отреагирует на нетривиальный ввод и даже используя функцию первый раз можешь предположить, в каком порядке передавать параметры.
    Ещё важный момент — это тестирование. В нашем воображаемом string-utils все функции покрыты тестами и нормально совместимы друг с другом: вывод одной можно подать на вход другой без всякого нежданчика. А если что-то случится, то фиксать будет одна команда разработчиков. А не так, что «совместимость с чужим пакетом пусть обеспечивают они, а не я».
    • +2
      А потом конечный JS бандл раздуется до 10 мб.
      • 0
        А делов-то: нужно всего-навсего написать программу, которая будет выбрасывать неиспользуемый код, как это делают линкеры компилируемых языков.
        Но, боюсь, для JS это будет совсем нереально из-за особенностей саомго языка.
        • 0
          webpack --optimize-minimize

          Вам в помощь
        • 0
          Достаточно использовать микромодульный подход к написанию библиотек. Тогда в пакет включаются исключительно те микромодули, что реально необходимы. При этом размер самой библиотеки может быть сколь угодно большим.
      • 0
        Да ладно! Пять лет назад никого не беспокоило подклеить к проекту весь jQuery ради какой-нибудь одной шняги типа $(".blahblah").height("100%"). Теперь каналы стали меньше? Вы таки уверены, что в собранном бандле каждая строчка вам приносит пользу и строго необходима?
        Опять же если остро стоит такой вопрос, можно делать зонтичные проекты. Типа string-utils — это список зависимостей на string-utils-core, string-utils-regex, string-utils-formatting и т. д. Не надо всё — укажите, что конкретно надо. Суть в том, что всё под одним именем, пишется одной командой в одном стиле и тестируется вместе.
  • +6
    Странно, что у автора этой заметки, ведущего инженера Stack Overflow, нет претензий к дичайшему гавнокоду в указанном "ключевом модуле" и что никто до сих пор его не выправил. Делать в цикле конкатенации по количеству необходимых символов с генерацией каждый раз нового String объекта на каждой итерации вместо "return ch.repeat(len — str.length) + str" — это какой-то эпикфейл.
    • +1
      String.prototype.repeat() поддерживается только в ES6+
      • 0
        Можно и без String#repeat сделать более оптимально.
      • +3
        Да, Вы правы. Тогда push в массив и join. Но никак не тот лютый трындец в сырце.
        • +2
          Вот тут уже подсуетились — https://github.com/azer/left-pad/pull/11
          В волшебной стране javascript даже join тормозит, на самом деле.
          • +2
            Ух жесть. Битовый сдвиг и конкатенация строки на себя же чтобы немного уменьшить алгоритмическую сложность и нагрузку на GC, но оставив по-сути проблему треша на своём месте… Мир JS жесток и беспощаден. )))
            • +1
              Никто не говорил, что фронтенд — это легко.
              • +1
                Если бы только фронтэнд, а то ведь его же везде пытаются пропихнуть
          • 0
            Я тут побенчмаркал слегка (clickable):

  • 0
    Ладно ещё когда пользуются left-pad, это можно понять, и велосипеды строить действительно не надо. Больше раздражает, когда собирают пакет, вкомпилировав туда кучу полифиллов, например, node.js Buffer, любезно подставляемый вебпаком, в результате чего пакет становится размером 50-100кб, хотя можно было бы всё сделать тремя строчками кода.
  • +9
    вместо того чтобы потратить 2 минуты и написать эту базовую функцию самому
    Если бы я занимался переписыванием базовых функций, на основную работу времени бы не оставалось.
    Плюс этим пришлось бы заниматься каждому разработчику, вместо использования уже готового решения.
  • +2
    Если бы можно было включить отдельную функцию из какой-нибудь библиотеки без того, чтобы подтягивать саму библиотеку и ее зависимости (и зависимости зависимостей), то делали бы не однострочные модули, а модули с множеством однострочных функций. Но так не получается, т.к. нет у интерпретатора шагов компиляции и линковки (желательно с сохраняемыми .o, .lib), отсюда и проблемы.

    Может, WebAssembly выручит каким-то образом когда-нибудь, вроде бы движение в нужном направлении. Во всяком случае, из кода выжимается весь сок — бинарное представление, и можно гарбаж-коллектнуть все ненужное. Практически объектный файл получается (ну, вроде того, если абстрагироваться от машинных инструкций).

    Но WA не избавит от 28К файлов. Которые, если я понимаю правильно, результат стратегии NPM по решению проблемы dependency hell — каждому модулю своя копия всех зависимостей, нужной версии. (ничего ж не путаю? оно так устроено?)

    В C++ и др. языках та же проблема (если с нуля собирать Hello, World на QT, то сколько всего сырцов будет? начиная от C-runtime), просто NPM реально легко позволяет создавать и публиковать однострочные модули и не позволяет (в силу специфики JS) распространять компилянты (.lib или .so/.dll). А делать и раздавать isNegativeZero на C++ — ну, нету для этого языка npmjs.com, packagist.org, cpan.org, да и порог вхождения повыше будет.

    В общем, Nodejs/NPM продемонстрировал ту же проблему, которая существует с момента создания первых языков программирования, просто она усугубилась:

    текущей средней сложностью программ,
    легкостью написания и публикации модулей,
    желанием повторно использовать код по максимуму
    и нехваткой времени (нежеланием) заниматься своей кодовой базой — импортнул модуль, и все ок, «одна ж строчка же ж!» — а за строчкой может 20 зависимостей притаилось. Но некогда, заказчик сидит на голове, трындит «вот Петров уже б давно закончил, он знаешь как быстро работает?!», и если не начнешь работать так же, то конкуретноспособность твоя до нуля опустится.
    • +3
      результат стратегии NPM по решению проблемы dependency hell — каждому модулю своя копия всех зависимостей, нужной версии. (ничего ж не путаю? оно так устроено?)
      Нет, ужé не «копия всех зависимостей»: npm, начиная с третьей версии, складывает все зависимости в кучу в подкаталог node_modules первого уровня до тех пор, пока два модуля не потребуют одну и ту же зависимость разных версий (и тогда она, и только она, достанется каждому такому модулю в виде своей копии нужной версии). Экономия.
      • +1
        А если 3 модуля хотят зависимость одной версии, а еще 3 — другой. Данная зависимость будет в 2х или в 6и копиях?
        • +1
          Выходит что в 4х, так что всё равно плохо.
  • 0
    Объясните пожалуйста, где можно применить модуль leftpad и для каких задач он вобще нужен? Что-то нигде не нашёл...
    • +5
      При выводе в консоль или текстовый файл табличных данных с выравниванием по правому краю.
      • 0
        Спасибо) Как-то даже не подумал в таком ключе...
        • +2
          Да пожалуйста. Вот вам еще кейс из жизни: формирование номера инвойса, ордера и т.д., вроде "INV-0000012".
          • 0
            А никакого аналога sprintf() там тоже нет что ли? А то sprintf(target, "INV-%07i", inv_no).
            • 0
              Есть, и не один. Но он не входит в стандартную библиотеку, и вам придется его поставить. Функциональность у него побогаче, но ставить модуль на 200-250 строк (и файлов на 15-20 — я только что глянул пару реализаций) если вам нужно функционала на 5-6 строк… ну, вы поняли, дальше идет взвешивание "за" и "против" для вашего конкретного проекта. Ну, а вообще учтите, sprintf будет сначала интерпретировать формат, а потом где-то в его дебрях будет происходить тот же leftpad.
          • +1
            Ну в статье правильно говорится — лучше подумать головой. Задача очень простая для нормального программиста.
  • 0
    Правда где-то посередине.
  • +5
    Нет, микромодули это замечательно. Пусть в модуле будет всего одна строка, но
    — эта строка не дублируется в каждом проекте
    — эта строка просматривается бòльшим числом глаз чем она просматривалась бы в одном проекте
    — эта строка имеет меньшую вероятность внести ошибку в проект нежели написанная с нуля, особенно неискушённым разработчиком
    — при нахождении бага в этой строке он будет исправлен сразу во всех проектах, использующих модуль
    Кроме того, не надо забывать что эта строка вполне может развиться в сотню.

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

    Проблема npm и других новомодных пакетных менеджеров в другом — зависимости никак не верифицируются, а зачастую даже не фиксируются по версиям. В лучшем случае любое обновление или новая установка может всё сломать (что и было успешно продемонстрировано, хотя на деле происходит постоянно), а если задуматься то по npm|pip|bower|cargo|… прилетает вообще неизвестно что — код может быть подменён в любой точке от автора до последней мили и вы об этом не узнаете, нет никакой возможности убедиться что сейчас прилетело то же что неделю назад, или вам прилетело то же что и Васе. Или Пете, который провёл аудит кода. И постфактум, когда катастрофа уже случилась, концов не найти.
    • 0
      Резюме: микромодули помогают избавиться от включения неиспользуемого кода в JavaScript, тем самым уменьшив размер файла. Но иногда это может привести к проблемам.
      • 0
        Контр-резюме: Любое решение иногда может привести к проблемам.
    • 0
      К сожалению часто микромодуль противоречит вот этому:
      — эта строка просматривается бòльшим числом глаз чем она просматривалась бы в одном проекте
      — эта строка имеет меньшую вероятность внести ошибку в проект нежели написанная с нуля, особенно неискушённым разработчиком

      такие вещи начинают использовать не задумываясь. Кроме того у JS есть серьёзные проблемы со сборкой и получением JS нужных размеров и нарезанных нужным образом… (1 Mb JS файл часто не вариант качать, как и 50-60 мелких файлов)
      Я короче в JS для Frontend отказался от любых сборочных систем, текущая ситуация меня не устраивает.
    • 0
      И как показала ситуация все это не шибко работает. Никто особо не просматривал этот left-pad, не считал оптимальность и не искал баги. Просто взяли и использовали. Потому что люди, которые готовы это делать обладают квалификацией, что написать самостоятельно. Остальные просто подключают.

      И источник проблемы указан верно: слабая стандартная библиотека. И тут силами коммьюнити объединять общепринятые решения в библиотеки, как например Underscore, но многие предпочитают решать конкретную проблему и делать микромодули.
  • 0
    Вот только к этому и стремились в сообществе node.js: не нужно писать свои велосипеды используй уже готовое. Поэтому сама ситуация не на столько ужасна, и первая на моей памяти. Единственное — раньше была проблема с папкой node_modules и то, что вес и вложенность превышала все разумные пределы.
  • 0
    С каких это пор «переиспользование кода» = «разучился программировать»? Очередное нытьё на тему «мы разучились программировать, мы все умрём». С какой стати использующие простой 11-строчный модуль по умолчанию приравниваются к тупым индусам? Вполне нормальный сценарий: человеку надоело копипастить эти 11 строк из проекта в проект, вот и заюзал модуль. «Проблема третьих лиц»? Что мешает проверить код этого микромодуля? Тем более всего 11 строк! А вот то, что у вас всё ломается при исчезновении какого-либо модуля из центрального репозитория — это проблема совсем не модулей-в-одну-строчку.
    • 0
      Да ладно вам, дайте людям поныть — повод же есть.
    • +1
      Проблема не в микросервисах и не в повторном использовании, просто в ноде нет качественной стандартной библиотеки. Нет скелета, который мог бы обрастать сторонними модулями. Была в начале года такая резонансная статья https://medium.com/@wob/the-sad-state-of-web-development-1603a861d29f, автор мог бы сейчас сказать "а я вас предупреждал"
  • +5
    Сегодня вышел комментарий от сотрудника компании Kik (я его перевел).
  • 0
    вместо того чтобы потратить 2 минуты и написать эту базовую функцию самому
    Если каждый разработчик сэкономит эти 2 минуты при решении своей задачи, то вся индустрия в целом сэкономит много человеко-часов. А таких функций может быть довольно много. На мой взгляд, автор просто не хочет замечать других проблем: отсутствие стандартизированной процедуры для верификации/ревью пакетов и ненадежная политика хранения пакетов.
  • +5
    И эти люди еще косо смотрят на PHP.
    В node_modules иногда и вовсе страшно заглядывать.
    • +1
      А зачем вы туда заглядываете?
      • +1
        По своему опыту, к сожалению, время от времени туда приходится заглядывать. Иногда нахожу баги в зависимостях или в зависимостях зависимостей, иногда просто пытаюсь понять, что же пошло не так и почему сборка не работает, либо код ведет себя плохо.
    • 0
      Всё же лучше злоупотреблять микромодулями на JavaScript из-за небольшой стандартной библиотеки, нежели злоупотреблять массивами на PHP из-за невозможности написать простой код (наподобие «{a: "b", c: 0xd}») для создания объекта на лету. (Да и удобство стандартной библиотеки PHP настолько не на высоте, что уж лучше она была мала и поощряла создание микромодулей.)
      • +1
        new class { public a = "b" }
        • +1
          (object)['a' => 'b']
    • +9
      Есть мнение, что JS это новый PHP.
  • 0
    Доброго времени суток
    Некогда не понимал зачем писать обертку для того, что есть и/или пишется одной строкой.
    На мой взгляд подобный код не имеет смысла оборачивать в функции.
    function isArray(a){
     return a && a.constructor === Array;
    }
    
    function pading(a, b) {
     return Array(b).join(" ") + a;
    }
    • +1
      Вот вы и ошиблись, isArray не все тесты проходит отсюда. Код для проверки возвращает:
      true true true true undefined false null undefined false false false false true
      Вместо четырёх true и остальных false.
      • 0
        Возможно, но первые четыре true, а остальные false, просто не boolean, но false.
        А если нужно boolean, хотя это и не имеет смысла, допишите:
        function isArray(a){
         return a && !!(a.constructor === Array);
        }
      • 0
        Извиняюсь забыл сказать о последней проверке:
        ==> 5 // Небольшой факт: Array.prototype сам является массивом:
        • +1
          Кажется, вы хотели написать выше:
          function isArray(a){
              return !!(a && a.constructor === Array);
          }

          Последний тест этот: Array.isArray({ __proto__: Array.prototype }); не пройден. У правильной функции там false. Единственно верный способ там указан:
          if (!Array.isArray) {
            Array.isArray = function(arg) {
              return Object.prototype.toString.call(arg) === '[object Array]';
            };
          }

          Это собственно говорит, что не всё так просто.
          • 0
            Извините, наверное мы говорим о разном, в стандартных ситуациях хватает и запроса к свойству 'constructor'
            В не стандартных ситуациях, для получения значения 'class' тоже использую '{}.toString.call()':
            function getTypeOf(a) {
             return (void 0 === a || null === a ? String(a) : {}.toString.call(a).replace(/\[.+?(\w+)\]/, '$1').toLowerCase()) || '';
            }

            Но сути дела это не меняет:
            зачем писать обертку для того, что есть и/или пишется одной строкой.
            • +1
              "зачем писать обертку для того, что есть и/или пишется одной строкой."

              В вашей реализации isArray есть: получение значения, булевская операция, получение значения свойства, строгое сравнение, получение значения глобального символа. Если же a будет чем-то вроде data.list или даже service.getList(o, p), то ваша одна строчка уже станет неузнаваемой, возможно придется ввести временную переменную. И если вдруг потом окажется, что проверка недостаточна, то во всех местах, где вы использовали этот код (напишите сходу регулярку на свой вариант или на варианты в ответах на ваш комментарий), нужно будет ее менять. А так — это будет четкий вызов isArray — читабельно, компактно и т.д. Разве что этот вызов окажется узким местом в цикле на миллион итераций. Но оптимизация с читабельностю и сопровождаемостью кода никогда не дружила, это отдельная от всего задача.
              • 0
                Спасибо
                Спасибо, за внимание, не понял вашего совета.
                function isArray(a){
                 return !!(a && a.constructor === Array);
                }

                где и что вы предлагаете дописать,
                и что делать регуляркой, возможно вы не мне.
                • 0
                  Что если окажется, что ваше решение содержит небольшую дырку или неопределенность? Например, объект {constructor:Array, content:'ha-ha-ha!'} пройдет проверку. И вы подумаете, и решите исправить на более точную формулировку. И эту уточненную строку вам придется вставить во всех местах, где была старая формулировка. И придется ее искать не просто поиском, а поиском по образцу (регуляркой), т.к. в конкретном применении может быть не "а", а "this.value" или "that.getItems()". И вот тут становится ясно, что на самом деле эти строки — это кусочек знания (о массивах, об исключительных случаях, и т.д.), и его лучше вынести за скобки, т.е. оформить в виде функции. И у такой функции будет такой приятный бонус, как временные переменные в виде аргументов (т.е. "that.getItems()" преобразуется в "a" из сигнатуры).
                  • 0
                    Спасибо
                    Теперь понятней, полностью с вами согласен
                    что на самом деле эти строки — это кусочек знания (о массивах, об исключительных случаях, и т.д.)

                    Хотя, так можно оборачивать и команду print.

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

          • 0
            Еще вариант
            "undefined" === typeof Array.prototype.isArray && (Array.prototype.isArray = true);
            Array.isArray || (Array.isArray = function(arg) {return arg.isArray;});

    • 0
      Ваш pading, кстати, делает не то, что обсуждаемый пакет (т.е. это другая задача), или это неправильно. Он слева добавляет b-1 пробелов, а не дополняет строку a указанным символом слева до указанной длины (которая может быть и меньше длины дополняемой строки). Вот вам и велик.
      • 0
        А если так,
        function pading(a, b, c) {
         return Array(b).join(c) + a;
        }

        Но это не принципиально, здесь пишу, как функцию только для понимания,
        хота сам использую только
        var str+=Array(2).join("0");

        или
        var str=Array(2).join("0") + str;

        Модули и лоадеры удобно использовать только на этапе наброска.
        А на этапе тестирования, отладки и оптимизации обычно стараюсь избавиться от зависимостей.
        Но это не означает, что я их не использую.
        • 0
          Ваш новый вариант уже лучше, все равно не отрабатывает основную задачу: добить строку до определенной длины.
          Кстати, в случае добивания числа нулями слева я обычно использую такой велик: String(1e9 + v).substr(-9). Ну, потом я добавляю проверку на отрицательные, потом на переполнение, и понеслась...
          На счет избавления от зависимостей, если библиотека выглядит здоровой — документация, фиксы, живые реплики автора, вменяемые тесты — чего б не использовать? А вот велики типа left-pad я бы публиковал со своим префиксом и сам бы себе использовал — просто ради удобства реюза в разных проектах.
          • 0
            Что то не так, вопрос не в варианте,
             function pading(a, b, с) {
              return (Array(b).join(c) + a).substr(-1*b);
             }

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

            И т.д и т.п.


            зачем писать обертку для того, что есть и/или пишется одной строкой.
            • 0
              Ваш вариант теперь выдает фиксированную ширину в любом случае, т.е. если исходная строка слишком широка, то она будет обрезана слева. Это может найти кой-какое применение, я бы назвал это фичей. Но если вы просто замените left-pad на это, то какие-то тесты провалятся (или баги в продакшн пролезут).
              А вот оптимизация — штука стремная, особенно если думать о ней слишком рано. Обычно это стоит делать после профайлера, и только в тонких местах. Во всех остальных местах оптимальным будет состояние кода, которое облегчает его сопровождение, а не выжимает десятые доли процента производительности за счет громоздких конструкций. Как пример, любая 3д-игра: видео-часть может даже до ассемблера докатиться ради оптимизации, а вот общее поведение, заскриптованные персонажи, реакции объектов на действия игрока — все это обычно пишется на языке, который значительно медленнее машинных кодов. Например, Lua или JavaScript.
              • 0
                function pading(a, b, с) {
                  return a.length < b ? (Array(b).join(c) + a).substr(-1*b): a;
                }

                Я наверное не правильно, выражаюсь, все мои примеры только импровизация на коленке, для подтверждения возможностей JavaScript.
                И любую задачу при желании можно описать одной строкой — если понимаю как, когда не понимаю ищу пример модуль или функцию изучаю, и если целесообразно добавляю фрагмент или вызов.
                • 0
                  "return Array(Math.max(0, b + 1 — a.length)).join(c ) + a;", хотя тут сразу выделяется доверие к типам аргументов a и b. Про любую задачу одной строкой — вы погорячились. Можно одной строкой некое решение выразить, ну "вроде работает, сойдет пока". А примеров, показывающих разницу между "вроде работает" и "проверенный, надежный код", можно много привести. Валидация URL или e-mail: короткая регулярка для начла, монструозные несколько страниц кода в конце. Хэш пароля: просто md5() для начала, а в конце — глубокое понимание криптостойкости и опять же одна строчка, только совсем другая. Ну, и так далее. На самом деле тривиальные случаи я почти всегда наколеночным решением закрываю. Но вообще предпочитаю код из 5+ токенов заменить на звучное название метода или функции, описывающее суть происходящего. Такой вот способ абстрагироваться.
                  • 0
                    Проверка длинны 'a.length < b', намного быстрей чем 'a.length' + 'Math.max' + 'Array.join'
                    function pading(a, b, с) {
                      return (b+=1,a.length < b ? (Array(b).join(c) + a).substr(-1*b): a);
                    }

                    Но в целом, для решении любой задачи существует два варианта, либо что-то делать для ее решения либо нет.
                    А программист добавит, пока все работает, ничего не меняй ;)
                    • 0
                      Не говорите "быстрей" рядом с "Array(b).join(...)" и с "-1*b". ;) На счет "работает — не трожь" согласен! Плохо только когда это тебе заказчик говорит, а тебе так хочется взять и отрефакторить все к чертовой бабушке...
                      • 0
                        Ну когда, есть заказчик это уже хорошо ;), нужно искать позитив.
                        А отрефакторить можно все хотя возникает вопрос зачем, если для изучения — причем заказчик, для удобства — меняю оптимизирую, тестирую — мотивирую заказчика.
                        Спасибо, было приятно пообщаться.
  • +3
    Я когда после java начал разбираться со зверинцем этим фронтэнд-разработчика тоже тихо прифигевал от этого садомазо с зависимостями для сокращения двух символов.
    Например, модуль keyMirror, который встречал постоянно у других. Зачем?
    писать keyMirror({blue: null, red: null});
    вместо {blue: "blue", red:"red"}
    Какой здесь чужой проверенный опыт?
    • 0
      Так вы поищите ответ. Такой же вопрос возникал у других, был задан на SO, ответ можно тут почитать: http://stackoverflow.com/questions/31345912/what-are-the-benefits-of-reacts-keymirror. Пример в Usage пакета демонстрирует функционал, а не юз кейз. Кроме того, там в шапке так и написано: выпилено из реакта чтобы избавиться от зависимостей. А в реакте у него конкретное предназначение (смотрите ответ на SO). Лет 15 назад, кстати, по тем же причинам я делал такую же болтанку, только писал сам, никаких нодов и нпмов не было. Был только стремный JSAN, и что-то как-то не пошло.
  • 0
    Вот недавно чинил велосипед с регуляркой для парсинга url от "умеющих" программировать программистов.
    • +3
      парсинг урл? регуляркой? еретики!
      думаете, эти 1300 строк просто так занимают место в JRE?
      Про ситуацию с библиотеками для работы со временем вообще молчу...
      • 0
        А я всегда парсю email регуляркой! И всегда потом начинаю есть себя с локтей… Шутка, пары раз хватило на всю жизнь, теперь только библиотеки.
      • 0
        Вот вот, и я так считаю, но программисты подумали, а что такого спарсить хост из урла и пропустили вполне валидные урлы типа http://example.com?ref=http://example.com а в сочетании с специфическим поведением ie7 при присвоении атрибута frame.src — это дало весьма занятный баг :)
  • +5
    http://left-pad.io/ теперь для этого есть сервис
    • 0
      Я ему пример со страницы же копирую: https://api.left-pad.io/?str=paddin%27%20oswalt&len=68&ch=@ — получаю {"message": "Could not parse request body into json: Unrecognized character escape \'\'\' (code 39)\n at [Source: [B@134efe97; line: 2, column: 21]"}. Плохой сервис, не экранирует кавычки.
    • +2
      У ребят большие планы на развитие:
      ## Are there any limits?

      Padding and the input string are limited to 1024 characters in the free
      version, because we have to monetize to have enough runway to launch
      `right-pad.io` in Q3 2017.
  • +1
    А что такое npm? Я вот не пользуюсь и бед не знаю.
    • 0
      Намекаете, что не пользуетесь Node.JS?
      Или действительно не можете загуглить npm?
      • 0
        В великих знаниях многие печали. Я бы хотел вернуться в то время, когда не знал 15 способов организации наследования в JS, базы данных вроде HFSQL и откуда в холодильнике берется еда. Не расстраивайте человека, пусть еще немного побудет счастливым.
        • +1
          Пользоваться Node.js, но не пользоваться npm — это скорее несчастье, нежели счастье.
          • +1
            О таком речь не шла. А если показалось, что шла, то извините, неточно выразился.
  • 0
    Никогда не понимал вот эту ситуацию с «28000 файлов». Каждый раз когда использую node.js и ставлю какой-то модуль, потом ужасаюсь тому, сколько всего он за собой потянул и сколько у меня теперь на винте файлов. А когда надо что-то найти, это вообще мрак.
    • 0
      А еще если у вас не супер-мега-быстрая файловая система, то все это добро еще и стартует кучу времени. Маленький проект на сотню строк, который в grunt по watch компилируется Бабелем — пара секунд проходит между сохранением и обновлением (на моей конкретной машине, ос, фс и т.д.). Похожие проблемы у Ruby и Python на Windows, кажется. Если в воркфлоу включить какой-нибудь Compass, то все, привет производительности труда.
  • 0
    Статья понятна, логична, правдива. Но это не поможет. Потому, что человек ленив, и вместо того, чтобы думать своей головой — предпочтет использовать готовое решение. Даже, если это повышает шанс отказа (о проблемах никто не думает — пока они не случаются). Даже, если готовое решение подходит плохо (забьют молотком).
    • НЛО прилетело и опубликовало эту надпись здесь
      • 0
        Я бы не стал мешать JS и .NET.
        В JS очень велики накладные расходы от включения в код неиспользуемых функций на стороне клиента, тогда как в Java и .NET я не вижу проблемы включения в зависимости довольно большой сборки с точки зрения производительности.
        Впрочем, чужие библиотеки я тоже стараюсь использовать как можно реже, а если и использую, то просто включая их код в свой проект, а не создавая зависимость.
        Из последних извращений:
        1. Потратил 2.5 дня на попытку подключить C++ MySQL адаптер и заставить его работать без глюков. Успеха не добился.
        2. Потратил 1.5 дня на написание собственного адаптера на C++, используя документацию с описанием сетевого протокола.
        3. Потом ещё пару дней на перевод написанного на C# с полной асинхронной реализацией написанного кода.
        4. Допиливание сторонней библиотеки, реализующей LinqToMySql, для использования с асинхронным кодом.
  • –1
    Я вообще не понимаю, зачем в наше время нужна динамическая линковка, например.
    • +1
      Ну, все же 50 процессов из разных экзешников разделяют заметное количество памяти (как минимум стандартный рантайм Си и еще что-нибудь, какой-нибудь user.dll в винде). Т.е. экономия памяти. А также, если линковать статически с тем же рантаймом, а потом вдруг в нем ошибку критическую найдут, то все пользователи будут качать все программы заново (руками или автоматом), многие будут пересобирать экзешники, и т.д. А так — подменил .dll или .so, и все сразу заиграло.
  • 0
    Считаю проблема поставлена некорректно. Обратимся к .NET и менеджеру NuGet: систем принципиально не дает удалить пакет или конкретную его версию из репозитория пакетов. Надо чтобы пакет не было видно? unlisting поможет убрать его из поиска, но пакет всегда можно будет установить как зависимость другого. И вопрос микропакетов тут совсем непричем, тоже самое может случиться и с довольно популярными библиотеками.
    • +1
      Если к ним придет постановление суда или просто очень убедительное (с точки зрения админа) письмо, то "принципиально не дает" и " всегда можно будет установить" резко перестануть быть актуальны.
  • +1
    Хорошо, что просто удалил, а если бы внедрил вредоносный код?
  • –1
    Доброго времени суток
    Пару дней назад я здесь написал:
    Некогда не понимал зачем писать обертку для того, что есть и/или пишется одной строкой.
    На мой взгляд подобный код не имеет смысла оборачивать в функции.
    Долго упирался, и не мог сформулировать, почему?

    Сейчас, дошло.
    Бездумное использование сторонних модулей без учета отказоустойчивости — по сути меня и тревожило.

    P.S. Вспомнил, сколько трачу времени на подбор и проверку подключаемых модулей.;)
  • 0
    Хочется разбавить разговор своим скромным мнением. Я программист со стажем 20+, а последние 6 лет еще и компанию свою развиваю как собственник и директор. Так вот хочется из мира бизнеса аналогию привести.
    Если собственник свои ключевые компетенции полностью делегировал сотрудникам и при этом не знает, как они это делают, — он попадает в полную зависимость от них. Выстроить процессы так, чтобы все были маленькими винтиками и делали свою маленькую работу можно. Вроде бы здорово все, все работает, можно не переживать. Но увольнение или болезнь ключевого сотрудника — беда.
    А выход какой? Выход в том, что собственник должен как минимум уметь выполнять сам все ключевые функции. Поруководить цехом, продать клиенту услугу и так далее. В бизнесе все это известно уже лет 100, так почему в программировании должно быть иначе?
    На мой взгляд умение искать аналогии в окружающем мире и использовать их на пользу проекту/продукту/компании и есть один из главных признаков высокой квалификации. А вовсе не умение найти в гугле функцию на 3 строки. Поэтому я никогда не доверяла экосистеме Руби и Node.js. Просто второе бомбануло раньше в силу более бурного роста.
    Также все вышесказанное заставляет меня глубоко сомневаться в успехе ИТ-компаний, во главе которых не стоят программисты с опытом. Непрограммист смотрит на программистов как на чудаковатых волшебников и совершенно теряется, если кто-то из них не может или не хочет что-то делать, а он сам — собственник — не может стряхнуть пыль и расчехлить, чтобы временно заменить бойца. Не то, чтобы надо было постоянно бегать и тушить пожары, это как раз плохо. Но надо иметь возможность. Не терять хватку помогает обучение новых сотрудников и студентов — и в процесс не лезешь, и квалификацию не теряешь.

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