Как я могу переопределить/расширить ReferenceError в JavaScript JavaScript?
Чтобы упростить отладку, я собираю все журналы консоли в Chrome, чтобы пользователи, отправляющие запись с обратной связью, также отправляли все журналы на наш сервер. Когда кто-то сталкивается с проблемой в производстве, я могу в первую очередь вернуть их к работе, чтобы затем я мог сесть и более тщательно пройти через все журналы, чтобы определить основную причину любой проблемы, с которой пользователь столкнулся в процессе производства.
Метод, который я использую для захвата журналов, включает в себя переопределение console.log, чтобы весь текст, введенный в первый аргумент, сохранялся в массиве, одновременно вызывая устаревшую функцию, чтобы я все еще мог видеть журналы в консоли.
Проблема заключается в том, когда случается случайное исключение. Они не включены в загруженные журналы, поэтому не всегда понятно, что вызвало проблему. Поэтому я попытался переопределить ReferenceError, написав функцию JavaScript, которая принимает функцию в качестве аргумента, затем возвращает новую функцию, которая с ней работает, например, сохранение данных в переменной, а затем вызывать устаревшую функцию как последний шаг:
function overrideException(legacyFn) {
/** arguments for original fn **/
return function() {
var args = [];
args[0] = arguments[0];
// pass in as arguments to original function and store result to
// prove we overrode the ReferenceError
output = ">> " + legacyFn.apply(this, args).stack;
return legacyFn.apply(this, arguments);
}
}
Чтобы проверить функцию overrideException, я запустил следующий код на консоли:
ReferenceError = overrideException(ReferenceError);
Затем я протестировал возвращенную функцию, новый ReferenceError, вручную сбросив ReferenceError:
throw new ReferenceError("YES!! IT WORKS! HAHAHA!");
Результирующий вывод на консоли:
ReferenceError: ДА! ОНО РАБОТАЕТ! Хахаха!
И проверка глобальной переменной output
из функции overrideException показывает, что она действительно выполнялась:
output
">> ReferenceError: YES!! IT WORKS! HAHAHA!
at ReferenceError (<anonymous>)
at new <anonymous> (<anonymous>:18:35)
at <anonymous>:2:7
at Object.InjectedScript._evaluateOn (<anonymous>:562:39)
at Object.InjectedScript._evaluateAndWrap (<anonymous>:521:52)
at Object.InjectedScript.evaluate (<anonymous>:440:21)"
Теперь, когда вещи начинают разваливаться. В нашем коде мы не узнаем, когда возникает неперехваченное исключение, поэтому я протестировал его, пытаясь запустить функцию, которая не существует:
ttt();
Результат:
ReferenceError: ttt не определен
Однако, в отличие от случая, когда мы явно бросаем ошибку, в этом случае функция не запускается, и у нас остается только устаревшая функциональность. Содержимое переменной output
будет таким же, как в первом тесте.
Итак, вопрос выглядит следующим образом: Как мы переопределяем функциональность ReferenceError, которую движок JavaScript использует для выброса ошибок, так что это то же самое, что мы используем, когда мы бросаем ReferenceError?
Имейте в виду, что моя проблема ограничена только Chrome в это время; Я создаю приложение Chrome Packaged.
Ответы
Ответ 1
По одной и той же причине я провел довольно много исследований: я хотел регистрировать ошибки и сообщать о них.
"Переопределение" собственного типа (ReferenceError
, String
или Array
) невозможно.
Chrome привязывает их до запуска любого Javascript, поэтому переопределение window.ReferenceError
не влияет.
Вы можете расширить ReferenceError
с помощью чего-то вроде ReferenceError.prototype.extension = function() { return 0; }
или даже переопределить toString
(для согласованности, попробуйте на странице, а не в Dev Tools).
Это вам не поможет.
Но не волноваться....
(1) Используйте window.onerror
, чтобы получить имя файла, 1-индексный номер строки и 0-индексированную позицию непустых ошибок, а также самую ошибку.
var errorData = [];
onerror = function(message, file, line, position, error) {
errorData.push({message:message, file:file, line:line, position:position, error:error});
};
См. fiddle для примера. Поскольку OP был специфичным для Chrome, он был протестирован только для работы в Chrome.
(2) Из-за улучшений в (1) это уже не нужно, но я оставляю эту вторую технику здесь для полноты, а так как onerror
не гарантируется работа для всех ошибок во всех браузерах. Иногда вы также увидите следующее:
var errors = [];
function protectedFunction(f) {
return function() {
try {
f.apply(this, arguments);
} catch(e) {
errors.push(e);
throw e;
}
};
}
setTimeout = protectedFunction(setTimeout);
setInterval = protectedFunction(setInterval);
etc...
FYI, все это очень похоже на то, что было сделано в библиотеке компилятора Google Closure, в goog.debug
, созданной во время разработки Gmail с намерением сделать именно это. Особый интерес представляют goog.debug.ErrorHandler
и goog.debug.ErrorReporter
.