2008-11-28 10 views
20

Ich schreibe eine globale Fehlerbehandlung "Modul" für eine meiner Anwendungen.Wie umgehe ich eine Funktion in Javascript?

Eines der Features, die ich haben möchte, ist in der Lage, eine Funktion einfach mit einem Try {} Catch {} Block zu wickeln, so dass alle Aufrufe an diese Funktion automatisch die Fehlerbehandlungscode, der meine ruft globale Protokollierungsmethode (Um den Code nicht überall mit try/catch-Blöcken zu verschmutzen).

Dies ist jedoch etwas jenseits meines Verständnisses der Low-Level-Funktion von Javascript, der .call und. Apply-Methoden und der "dieses" Schlüsselwort.

Ich schrieb diesen Code, basierend auf Function.wrap Methode Prototype:

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

, die wie folgt verwendet:

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

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

Dieser Code funktioniert perfekt. Es druckt 6 aus und ruft dann meinen globalen Fehlerhandler auf.

Meine Frage ist ... Wird dies etwas brechen, wenn die Funktion, die ich umschließe, in einem Objekt ist, und es verwendet den Operator "this"? Ich mache mir etwas Sorgen, seit ich anrufe. Ich gebe etwas dort hin, ich fürchte, das könnte etwas kaputt machen.

Antwort

41

persönlich statt verschmutzenden gebautet Objekte ich mit einem Dekorateur Technik gehen würde:

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

Sie können es so verwenden:

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); 

Aber wenn Sie wie Modifikationen der Prototypen fühlen möchten, können Sie schreibe es so:

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); 
    } 
    }; 
}; 

Offensichtliche Verbesserung wird sein, mak zu parametrisieren eSafe(), so dass Sie angeben können, welche Funktion im catch-Block aufgerufen werden soll.

+0

OK, so neben der Präferenz der Verschmutzung oder nicht ... Ihre letzte Code-Schnipsel sieht identisch mit mir. Sollte ich daraus verstehen, dass mein Code mit Objekten und dem Schlüsselwort "this" funktioniert? Danke! –

+2

Ja: Sie übergeben sowohl "this" als auch Originalargumente an die umschlossene Methode. Aber Sie geben das Ergebnis nicht zurück und machen das Wrapping unvollständig. Es spielt jedoch keine Rolle, wenn Sie eine Funktion umbrechen, die keinen Wert zurückgibt. –

0

Soweit ich die Namespaces verschmutze, werde ich sie tatsächlich etwas mehr verschmutzen ... Da alles, was in JS passiert, durch ein Ereignis irgendeiner Art ausgelöst wird, plane ich, meine magische Wrapper-Funktion aufzurufen aus der Prototype Event.observe() -Methode, so dass ich sie nicht überall aufrufen muss.

Ich sehe natürlich die Nachteile von all dem, aber dieses spezielle Projekt ist sowieso stark an Prototype gebunden, und ich möchte, dass dieser Fehlerhandler-Code so global wie möglich ist, also ist das keine große Sache.

Danke für Ihre Antwort!

+0

Neben unbeabsichtigten Namenskonflikten können verschmutzende eingebaute Objekte zu seltsamen Fehlern auf Sprachniveau führen. Das ist der Grund, warum die Arbeit am neuen JavaScript-Standard Diskussionen über das "Schließen" von eingebauten Objekten auf Modifikationen wiederbelebt hat => zukunftssicherer Code sollte diese Technik vermeiden. –

2

Object.extend (Function.prototype, { Object.extend in der Google Chrome-Konsole gibt mir 'undefined' Nun, hier ist einige Arbeitsbeispiel:

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)); 

so statt Object.extend (Function.prototype, {...}) sie es mögen: Function.prototype.extend = {}

6

2017 Antwort: nur ES6 verwenden.Angesichts der folgenden Demo-Funktion:

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

Sie können Ihre eigenen Wrapper-Funktion, ohne dass externe Bibliotheken machen:

var wrap = function(someFunction){ 
    var wrappedFunction = function(){ 
    var args = [...arguments].splice(0) 
    console.log(`You're about to run a function with these arguments: \n  ${args}`) 
    return someFunction(args) 
    } 
    return wrappedFunction 
} 

Im Einsatz:

doThing = wrap(doThing) 

doThing('one', {two:'two'}, 3) 

2016 Antwort: Verwenden Sie die wrap Modul:

Im folgenden Beispiel wickle ich process.exit(), aber das funktioniert glücklich mit jeder anderen Funktion (einschließlich Browser JS auch).

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);