Как я могу переопределить/расширить 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.