2008-11-28 10 views
20

Estoy escribiendo un "módulo" de manejo de errores global para una de mis aplicaciones.¿Cómo envuelvo una función en Javascript?

Una de las características que quiero tener es poder envolver fácilmente una función con un bloque Try {} Catch {}, para que todas las llamadas a esa función tengan automáticamente el código de manejo de errores que llamará a mi método de registro global. (Para evitar contaminar el código en todas partes con bloques try/catch).

Esto es, sin embargo, un poco más allá de mi comprensión del funcionamiento de bajo nivel de Javascript, los métodos .call y .apply, y la palabra clave "this".

de escribir este código, basado en el método de Function.wrap Prototipo:

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

que se utiliza como esto:

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

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

Ese código funciona perfectamente. Imprime 6 y luego llama a mi controlador de error global.

Mi pregunta es ... ¿Esto romperá algo cuando la función que estoy envolviendo está dentro de un objeto, y usa el operador "this"? Estoy un poco preocupado ya que estoy llamando. Aplicar, pasar algo allí, me temo que esto puede romper algo.

Respuesta

41

personalmente en lugar de objetos contaminantes incorporadas Me gustaría ir con una técnica decorador:

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

Usted puede usarlo como esa:

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

Pero si usted se siente como la modificación de prototipos, puede escríbelo así:

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

La mejora obvia será parametrizar mak eSafe() para que pueda especificar qué función llamar en el bloque catch.

+0

Bien, así que además de la preferencia de contaminar o no ... Su último fragmento de código parece idéntico al mío. ¿Debo entender de esto que mi código funciona con objetos y la palabra clave "this"? Gracias! –

+2

Sí: pasa ambos argumentos "this" y original al método envuelto. Pero no devuelve su resultado, haciendo que el envolvimiento sea incompleto. Pero no importa si ajusta una función que no devuelve un valor. –

0

En cuanto a la contaminación de los espacios de nombres, en realidad los voy a contaminar un poco más ... Dado que todo lo que sucede en JS es iniciado por un evento de algún tipo, planeo llamar a mi envoltura mágica desde dentro del método Prototype Event.observe(), así que no necesito llamarlo a todas partes.

Veo las desventajas de todo esto, por supuesto, pero este proyecto en particular está estrechamente relacionado con Prototype de todos modos, y quiero que este código manejador de errores sea lo más global posible, así que no es gran cosa.

Gracias por su respuesta!

+0

Además de los conflictos de nombres no deseados, la contaminación de objetos incorporados puede provocar errores extraños en el nivel de idioma. Es por eso que trabajar en un nuevo estándar de JavaScript revivió las discusiones sobre el "cierre" de objetos incorporados a modificaciones => el código a prueba de futuro debería evitar esta técnica. –

2

Object.extend (Function.prototype, { Object.extend en la consola de Google Chrome me da 'Indefinido' Bueno aquí está un cierto ejemplo de trabajo:

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

así que en vez de Object.extend (Function.prototype, {...}) hacerlo como: Function.prototype.extend = {}

6

respuesta: sólo tiene que utilizar ES6.Dada la siguiente función de demostración:

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

Usted puede hacer su propia función de contenedor sin necesidad de bibliotecas externas:

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 
} 

En uso:

doThing = wrap(doThing) 

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

2016 respuesta: Utilice el wrap módulo:

En el siguiente ejemplo, estoy ajustando process.exit(), pero esto funciona felizmente con cualquier otra función (incluido el navegador JS también).

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