36

私は自分のアプリケーションの1つにグローバルエラー処理「モジュール」を書いています。

私が望んでいる機能の1つは、関数をブロックで簡単にラップできるようにすることです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 を実行すると「未定義」と表示されます。実際の例を次に示します

    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 に答える
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 に答える
-2

名前空間を汚染する限り、実際にはもう少し汚染します... JS で発生するすべてのことは何らかのイベントによって開始されるため、プロトタイプ イベント内から魔法のラッパー関数を呼び出すことを計画しています。 .observe() メソッドなので、どこでも呼び出す必要はありません。

もちろん、これらすべてのマイナス面はわかりますが、この特定のプロジェクトはいずれにしても Prototype と強く結びついており、このエラー ハンドラー コードをできるだけグローバルにしたいので、大したことではありません。

ご回答有難うございます!

于 2008-11-28T21:26:09.723 に答える