36

我正在为我的一个应用程序编写一个全局错误处理“模块”。

我想要的功能之一是能够轻松地用try{} catch{}块包装函数,以便对该函数的所有调用将自动具有将调用我的全局日志记录方法的错误处理代码。(为了避免使用 try/catch 块污染代码)。

.call然而,这稍微超出了我对 JavaScript、 and.apply方法和this关键字的低级功能的理解。

我写了这段代码,基于原型的Function.wrap方法:

Object.extend(Function.prototype, {
  TryCatchWrap: function() {
    var __method = this;
    return function() {
            try { __method.apply(this, arguments) } catch(ex) { ErrorHandler.Exception(ex); }
    }
  }
});

这是这样使用的:

function DoSomething(a, b, c, d) {
    document.write(a + b + c)
    alert(1/e);
}

var fn2 = DoSomething.TryCatchWrap();
fn2(1, 2, 3, 4);

该代码完美运行。它打印出 6,然后调用我的全局错误处理程序。

我的问题是:当我包装的函数在一个对象中并且它使用“this”运算符时,这会破坏什么吗?我有点担心,因为我正在调用.apply,在那里传递一些东西,我担心这可能会破坏一些东西。

4

7 回答 7

63

我个人不会污染内置对象,而是使用装饰器技术:

var makeSafe = function(fn){
  return function(){
    try{
      return fn.apply(this, arguments);
    }catch(ex){
      ErrorHandler.Exception(ex);
    }
  };
};

你可以这样使用它:

function fnOriginal(a){
  console.log(1/a);
};

var fn2 = makeSafe(fnOriginal);
fn2(1);
fn2(0);
fn2("abracadabra!");

var obj = {
  method1: function(x){ /* do something */ },
  method2: function(x){ /* do something */ }
};

obj.safeMethod1 = makeSafe(obj.method1);
obj.method1(42);     // the original method
obj.safeMethod1(42); // the "safe" method

// let's override a method completely
obj.method2 = makeSafe(obj.method2);

但是如果你确实想修改原型,你可以这样写:

Function.prototype.TryCatchWrap = function(){
  var fn = this; // because we call it on the function itself
  // let's copy the rest from makeSafe()
  return function(){
    try{
      return fn.apply(this, arguments);
    }catch(ex){
      ErrorHandler.Exception(ex);
    }
  };
};

明显的改进将是参数化 makeSafe() 以便您可以指定在 catch 块中调用什么函数。

于 2008-11-28T21:13:26.710 回答
11

2017 回答:只使用 ES6。给定以下演示功能:

function doThing(){
  console.log(...arguments)
}

您可以制作自己的包装函数,而无需外部库:


function wrap(someFunction){
  function wrappedFunction(){
    var newArguments = [...arguments]
    newArguments.push('SECRET EXTRA ARG ADDED BY WRAPPER!')
    console.log(`You're about to run a function with these arguments: \n  ${newArguments}`)
    return someFunction(...newArguments)
  }
  return wrappedFunction
}

正在使用:

doThing('one', 'two', 'three')

正常工作。

但是使用新的包装函数:

const wrappedDoThing = wrap(doThing)
wrappedDoThing('one', 'two', 'three')

回报:

one two three SECRET EXTRA ARG ADDED BY WRAPPER!

2016 回答:使用wrap模块:

在下面的示例中,我正在包装process.exit(),但这可以与任何其他功能(也包括浏览器 JS)一起使用。

var wrap = require('lodash.wrap');

var log = console.log.bind(console)

var RESTART_FLUSH_DELAY = 3 * 1000

process.exit = wrap(process.exit, function(originalFunction) {
    log('Waiting', RESTART_FLUSH_DELAY, 'for buffers to flush before restarting')
    setTimeout(originalFunction, RESTART_FLUSH_DELAY)
});

process.exit(1);
于 2016-05-17T12:11:04.763 回答
2

Object.extend(Function.prototype, { Google Chrome 控制台中的Object.extend给了我 'undefined' 好吧,这里有一些工作示例:

    Boolean.prototype.XOR =
//  ^- Note that it's a captial 'B' and so
//      you'll work on the Class and not the >b<oolean object
        function( bool2 ) { 

           var bool1 = this.valueOf();
           //         'this' refers to the actual object - and not to 'XOR'

           return (bool1 == true   &&   bool2 == false)
               || (bool1 == false   &&   bool2 == true);
        } 

alert ( "true.XOR( false ) => " true.XOR( false ) );

所以代替 Object.extend(Function.prototype, {...}) 这样做:Function.prototype.extend = {}

于 2012-10-28T15:25:17.993 回答
1

老式的函数包装:

//Our function
function myFunction() {
  //For example we do this:
  document.getElementById('demo').innerHTML = Date();
  return;
}

//Our wrapper - middleware
function wrapper(fn) {
  try {
    return function(){
      console.info('We add something else', Date());
      return fn();
    }
  }
  catch (error) {
    console.info('The error: ', error);
  }
}

//We use wrapper - middleware
myFunction = wrapper(myFunction);

ES6 风格也一样:

//Our function
let myFunction = () => {
  //For example we do this:
  document.getElementById('demo').innerHTML = Date();
  return;
}

//Our wrapper - middleware
const wrapper = func => {
  try {
    return () => {
      console.info('We add something else', Date());
      return func();
    }
  }
  catch (error) {
    console.info('The error: ', error);
  }
}

//We use wrapper - middleware
myFunction = wrapper(myFunction);
于 2019-05-05T03:44:07.633 回答
0

以下包装实用程序采用一个函数,使开发人员能够注入代码或包装原始代码:


function wrap(originalFunction, { inject, wrapper } = {}) {

    const wrapperFn = function(...args) {
        if (typeof inject === 'function') {
            inject(originalFunction, this);
        }
        if (typeof wrapper === 'function') {
            return wrapper(originalFunction, this, args);
        }
        return originalFunction.apply(this, args);
    };

    // copy the original function's props onto the wrapper
    for(const prop in originalFunction) {
      if (originalFunction.hasOwnProperty(prop)) {
        wrapperFn[prop] = originalFunction[prop];
      }
    }
    return wrapperFn;
}

使用示例:


// create window.a()
(function() {

    const txt = 'correctly'; // outer scope variable
    
    window.a = function a(someText) { // our target
        if (someText === "isn't") {
            throw('omg');
        }
        return ['a', someText, window.a.c, txt].join(' ');
    };
    
    window.a.c = 'called'; // a.c property example
})();

const originalFunc = window.a;
console.log(originalFunc('is')); // logs "a is called correctly"

window.a = wrap(originalFunc);
console.log(a('is')); // logs "a is called correctly"

window.a = wrap(originalFunc, { inject(func, thisArg) { console.log('injected function'); }});
console.log(a('is')); // logs "injected function\na is called correctly"

window.a = wrap(originalFunc, { wrapper(func, thisArg, args) { console.log(`doing something else instead of ${func.name}(${args.join(', ')})`); }});
console.log(a('is')); // logs "doing something else instead of a(is)"

window.a = wrap(originalFunc, {
    wrapper(func, thisArg, args) {
        try {
            return func.apply(thisArg, args);
        } catch(err) {
            console.error('got an exception');
        }
    }
});
a("isn't"); // error message: "got an exception"

最后一个例子演示了如何用 try-catch 子句包装你的函数

于 2020-12-09T13:51:02.900 回答
0

这是一个 ES6 风格:

const fnOriginal = (a, b, c, d) => {
    console.log(a);
    console.log(b);
    console.log(c);
    console.log(d);
    return 'Return value from fnOriginal';
};


const wrapperFunction = fn => {
    return function () {
        try {
            const returnValuFromOriginal = fn.apply(this, arguments);
            console.log('Adding a new line from Wrapper :', returnValuFromOriginal);
        } catch (ex) {
            ErrorHandler.Exception(ex);
        }
    };
};

const fnWrapped = wrapperFunction(fnOriginal);
fnWrapped(1, 2, 3, 4);
于 2021-05-06T06:47:57.787 回答
-2

就污染命名空间而言,我实际上会再污染它们......因为在 JS 中发生的所有事情都是由某种事件引发的,所以我打算从 Prototype Event 中调用我的神奇包装函数.observe() 方法,所以我不需要到处调用它。

当然,我确实看到了所有这些的缺点,但是这个特定的项目无论如何都与 Prototype 密切相关,而且我确实希望这个错误处理程序代码尽可能全局化,所以这没什么大不了的。

感谢您的回答!

于 2008-11-28T21:26:09.723 回答