
Но у неё есть пару неудобств, которые можно очень легко исправить. Это:
- Ошибки, когда консоль не определена
- Невозможность использовать вне контекста
- Невозможность отключить во время 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