Pull to refresh

Делаем консоль чуточку удобнее

Reading time4 min
Views7.1K
Практически все Javascript-программисты пользуются консолью в браузерах. Консоль встроена в Хром, Оперу, IE и устанавливается с Firebug в Фоксе.
Но у неё есть пару неудобств, которые можно очень легко исправить. Это:
  • Ошибки, когда консоль не определена
  • Невозможность использовать вне контекста
  • Невозможность отключить во время production
  • некроссбраузерность

Исправим эти проблемы легко и быстро!



В первую очередь нам необходимо подменить объект консоли на свой объект. Оригинальную консоль сохраним в переменной original
(function () {
   var global   = this;
   var original = global.console;
   // переопределяем консоль
   var console  = global.console = {};
})();


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

(function () {
   var global   = this;
   var original = global.console;
   var console  = global.console = {};
   
   // список методов
   var methods = ['assert', 'count', 'debug', 'dir', 'dirxml', 'error', 'group', 'groupCollapsed', 'groupEnd', 'info', 'log', 'markTimeline', 'profile', 'profileEnd', 'table', 'time', 'timeEnd', 'trace', 'warn'];
   
   // обход все элементов массива в обратном порядке
   for (var i = methods.length; i--;) {
      // обратите внимание, что обязательно необходима анонимная функция,
      // иначе любой метод нашей консоли всегда будет вызывать метод 'assert'
      (function (methodName) {
         // определяем новый метод
         console[methodName] = function () {
            // только если консоль доступна и есть нужный метод
            if (original && methodName in original) {
               // вызываем оригинальный метод консоли
               original[methodName].apply(original, arguments);
            }
         };
      })(methods[i]);
   }
})();


Таким образом мы не только избавились от ошибок в случае неопределённой консоли, но и решили вторую проблему — возможность использования консоли вдали от контекста, например:

// При клике вызвать console.log
$('#my-element').click(console.log);

// Короткий алиас
var log = console.log;
for (var i = 0; i < 10; i++) log(i);


Без нашего переопределения что в первом, что во втором случае мы бы получили ошибку "Illegal invocation".

Следующее, что нам необходимо сделать — это добавить возможность отключение дебага во время production. Это делается двумя строчками:

(function () {
   // ..
   var console  = global.console = {};
   console.production = false;
   
   // ...
         console[methodName] = function () {
            // только если консоль доступна и есть нужный метод И ЕСЛИ ЭТО НЕ РАБОЧИЙ КОД
            if (original && methodName in original && !console.production) {
               // вызываем оригинальный метод консоли
               original[methodName].apply(original, arguments);
   // ..
})();


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

console.production = true;


Особенности браузеров


Говноосёл


IE 8 и IE 9 как всегда отличились. Если вызвать следующий код:
alert(typeof console.log);

То нормальные браузеры выведут «function», а осёл — «object». У object нету метода apply, потому самый простой способ обойти этот баг — вызвать метод из прототипа функции. Немного магии:
original[methodName].apply(original, arguments);
// заменяем на:
Function.prototype.apply.call(original[methodName], original, arguments);


Firebug


Firebug не позволяет переопределить стандартную переменную. Потому решается очень простым фиксом. Перед присвоением необходимо удалить свойство:

   var original = global.console;
   var console  = global.console = {};
   // => 
   var original = global.console;
   delete global.console;
   var console  = global.console = {};


Добавляем новые методы


Многие знают, что в самых прогрессивных браузерах нету методов time и timeEnd. Портируем их из Firebug:

   // ...
   var original = global.console;
   var console  = global.console = {};
   if (original && !original.time) {
      original.time = function(name, reset){
         if (!name) return;
         var time = new Date().getTime();
         if (!console.timeCounters) console.timeCounters = {};
         
         var key = "KEY" + name.toString();
         if(!reset && console.timeCounters[key]) return;
         console.timeCounters[key] = time;
      };

      original.timeEnd = function(name){
         var time = new Date().getTime();
            
         if(!console.timeCounters) return;
         
         var key  = "KEY" + name.toString();
         var timeCounter = console.timeCounters[key];
         
         if (timeCounter) {
            var diff = time - timeCounter;
            var label = name + ": " + diff + "ms";
            console.info(label);
            delete console.timeCounters[key];
         }
         return diff;
      };
   }
   
   
   // список методов
   var methods = // ...


Теперь даже ишаки научились считать время.

Окончательный код у нас получился следующим:

(function () {
   var global   = this;
   var original = global.console;
   if ('console' in global) delete global.console;
   var console  = global.console = {};
   console.production = false;
   
   if (original && !original.time) {
      original.time = function(name, reset){
         if (!name) return;
         var time = new Date().getTime();
         if (!console.timeCounters) console.timeCounters = {};
         
         var key = "KEY" + name.toString();
         if(!reset && console.timeCounters[key]) return;
         console.timeCounters[key] = time;
      };

      original.timeEnd = function(name){
         var time = new Date().getTime();
            
         if(!console.timeCounters) return;
         
         var key  = "KEY" + name.toString();
         var timeCounter = console.timeCounters[key];
         
         if (timeCounter) {
            var diff = time - timeCounter;
            var label = name + ": " + diff + "ms";
            console.info(label);
            delete console.timeCounters[key];
         }
         return diff;
      };
   }
   
   var methods = ['assert', 'count', 'debug', 'dir', 'dirxml', 'error', 'group', 'groupCollapsed', 'groupEnd', 'info', 'log', 'markTimeline', 'profile', 'profileEnd', 'table', 'time', 'timeEnd', 'trace', 'warn'];
   
   for (var i = methods.length; i--;) {
      (function (methodName) {
         console[methodName] = function () {
            if (original && methodName in original && !console.production) {
               Function.prototype.apply.call(original[methodName], original, arguments);
            }
         };
      })(methods[i]);
   }
})();


Теперь консоль не вызывает ошибок, её можно использовать вне контекста, отключать на боевом сервере и считать время в IE)

Пользуйтесь на здоровье, лицензия на код — LGPL/MIT, лицензия на текст — CC BY 3.0

Github: github.com/theshock/console-cap
Tags:
Hubs:
+87
Comments35

Articles