Статьи

Deb.js: самый маленький отладчик в мире

Мы, как разработчики, пишем код. Теперь мы не просто пишем, мы также проверяем, работает ли написанный код. Мы тратим много времени и прилагаем много усилий, чтобы наши программы делали то, что должны делать. Этот процесс отладки часто бывает болезненным. Особенно, если мы не используем надлежащие инструменты. Чтобы помочь с этой проблемой, в сегодняшней статье представлена Deb.js , небольшая библиотека JavaScript, которая помогает вам выполнять отладку из браузера.

Давайте начнем с создания простой страницы с некоторым взаимодействием JavaScript. Мы создадим форму с двумя полями и кнопкой. Как только пользователь нажмет на кнопку, мы соберем данные и выведем сообщение в консоль. Вот разметка страницы:

1
2
3
4
5
6
7
8
<form>
    <label>Name:</label>
    <input type=»text» name=»name» />
    <label>Address:</label>
    <input type=»text» name=»address» />
    <input type=»button» value=»register» />
</form>
<div data-element=»output»></div>

Чтобы упростить пример, мы будем использовать jQuery для выбора DOM и событий. Мы обернем функциональность в следующем модуле:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
var Module = {
    collectData: function(cb) {
        var name = $(‘[name=»name»]’).val();
        var address = $(‘[name=»address»]’).val();
        if(name != » && address != ») {
            cb(null, { name: name, address: address });
        } else {
            cb({msg: ‘Missing data’});
        }
    },
    error: function(err) {
        $(‘[data-element=»output»]’).html(err.msg);
    },
    success: function(data) {
        $(‘[data-element=»output»]’).html(‘Hello ‘ + data.name + ‘!’);
    }
}

Функция collectData получает значение из полей и проверяет, набрал ли пользователь что-либо. Если нет, то он запускает обратный вызов с объектом, содержащим короткое сообщение об ошибке. Если все в порядке, он отвечает null в качестве первого параметра и другим объектом в качестве второго, содержащего данные. Разработчик, использующий модуль, должен проверить, не передан ли объект ошибки. Если нет, тогда начните использовать второй полученный аргумент. Например:

1
2
3
4
5
6
7
8
9
$(‘[value=»register»]’).on(‘click’, function() {
    Module.collectData(function(err, data) {
        if(typeof err === ‘object’) {
            Module.error(err);
        } else {
            Module.success(data);
        }
    });
});

Итак, мы проверяем, является ли параметр err объектом, и если да, то мы показываем сообщение. Если мы внимательно посмотрим на код, мы обнаружим проблему, но давайте проверим, как все работает:

Когда данных нет, наш скрипт работает как положено. Под формой отображается текст с Missing data . Однако, если мы добавим что-то в поля и нажмем кнопку, мы получим: Uncaught TypeError: Cannot read property 'msg' of null , message. Теперь давайте выследим ошибку и удалим ее.

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

Похоже, что метод error нашего модуля получает что-то, что является null . И, конечно же, null не имеет свойства msg . Вот почему браузер выдает ошибку. Есть только одно место, где вызывается функция error . Давайте установим точку останова и посмотрим, что произойдет:

Похоже, мы получили правильный объект data и error равна null , что является правильным поведением. Итак, проблема должна быть где-то в предложении if . Давайте добавим console.log и посмотрим, пойдем ли мы в правильном направлении:

1
2
3
4
5
6
7
8
Module.collectData(function(err, data) {
    console.log(typeof err);
    if(typeof err === ‘object’) {
        Module.error(err);
    } else {
        Module.success(data);
    }
});

И действительно, typeof err возвращает object . Вот почему мы всегда показываем ошибку.

И вуаля, мы нашли проблему. Нам просто нужно изменить оператор if на if (err) и наш маленький эксперимент будет работать как положено.

Однако такой подход иногда бывает сложным, поэтому рассмотрим следующие указатели:

  • Как мы видели, мы закончили регистрацию переменной. Таким образом, установка точки останова не всегда достаточно. Мы должны прыгнуть на консоль тоже. В то же время мы должны взглянуть на наш редактор кода и панель отладки Chrome. Это несколько разных мест для работы, которые могут раздражать.
  • Это также проблема, если в консоль вошло много данных. Иногда сложно найти необходимую информацию.
  • Этот подход не помогает, если у нас есть проблемы с производительностью. Чаще всего нам нужно знать время выполнения.

Остановка программы во время выполнения и проверка ее состояния бесценны, но у Chrome нет возможности узнать, что мы хотим видеть. Как и в нашем случае, мы должны дважды проверить предложение if . Разве не было бы лучше, если бы у нас был инструмент, напрямую доступный из нашего кода? Библиотека, которая приносит подобную информацию, как отладчик, но живет внутри консоли? Ну, Deb.js может быть ответом на этот вопрос.

Deb.js — это небольшой кусочек JavaScript-кода, уменьшенный до 1,5 килобайта, который отправляет информацию на консоль. Он может быть прикреплен к каждой функции и распечатывает:

  • Место и время выполнения функции
  • Трассировки стека
  • Форматированный и сгруппированный вывод

Давайте посмотрим, как выглядит наш пример, когда мы используем Deb.js:

Мы снова видим точные переданные аргументы и трассировку стека. Однако обратите внимание на изменения в консоли. Мы работаем над нашим кодом, выясним, где может быть проблема и добавим .deb() после определения функции. Обратите внимание, что тип err размещен внутри функции. Таким образом, нам не нужно искать его. Вывод также сгруппирован и раскрашен. Каждая функция, которую мы хотим отладить, будет напечатана другим цветом. Давайте теперь исправим нашу ошибку и разместим еще один deb() чтобы посмотреть, как он выглядит.

Теперь у нас есть две функции. Мы могли бы легко различить их, потому что они разных цветов. Мы видим их ввод, вывод и время выполнения. Если есть какие-либо операторы console.log , мы увидим их внутри функций, в которых они встречаются. Существует даже возможность оставить описание для лучшего распознавания функций.

Обратите внимание, что мы использовали debc а не deb . Это та же функция, но выход рушится. Если вы начнете использовать Deb.js , вы очень скоро обнаружите, что вы не всегда хотите видеть все.

Первоначальная идея пришла из сообщения в блоге Реми Шарпа о том, где найти console.log . Он предположил, что мы можем создать новую ошибку и получить оттуда трассировку стека:

01
02
03
04
05
06
07
08
09
10
11
12
[‘log’, ‘warn’].forEach(function(method) {
  var old = console[method];
  console[method] = function() {
    var stack = (new Error()).stack.split(/\n/);
    // Chrome includes a single «Error» line, FF doesn’t.
    if (stack[0].indexOf(‘Error’) === 0) {
      stack = stack.slice(1);
    }
    var args = [].slice.apply(arguments).concat([stack[1].trim()]);
    return old.apply(console, args);
  };
})

Оригинальный пост можно найти в блоге Реми . Это особенно полезно, если мы разрабатываем в среде Node.js.

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

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
Function.prototype.awesome = function() {
    var original = this;
    return function() {
        console.log(‘before’);
        var args = Array.prototype.slice.call(arguments, 0);
        var res = original.apply(this, args);
        console.log(‘after’);
        return res;
    }
}
 
var doSomething = function(value) {
    return value * 2;
}.awesome();
console.log(doSomething(42));

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

Остальная часть реализации Deb.js — просто украшение. Некоторые браузеры поддерживают console.group и console.groupEnd что очень помогает для визуального представления логирования. Chrome даже дает нам возможность раскрашивать напечатанную информацию в разные цвета.

Я верю в использование отличных инструментов и инструментов. Браузеры — это умные инструменты, разработанные умными людьми, но иногда нам нужно нечто большее. Deb.js стал крошечной утилитой и успешно помог мне ускорить процесс отладки. Это, конечно, с открытым исходным кодом. Не стесняйтесь сообщать о проблемах или выдвигать запросы .

Спасибо за прочтение.