2011-03-10 17 views
10

Si tengo una función arbitraria myFunc, lo que pretendo hacer es reemplazar esta función con una llamada envuelta que ejecute código antes y después de que se ejecute, p.Cómo envolver una función en Javascript/jQuery

// note: psuedo-javascript 

var beforeExecute = function() { ... } 
var afterExecute = function() { ... } 

myFunc = wrap(myFunc, beforeExecute, afterExecute); 

Sin embargo, no tengo una implementación de la función wrap requerido. ¿Hay algo que ya exista en jQuery como este (he echado un vistazo a los documentos pero no puedo ver nada)? Alternativamente, ¿alguien sabe de una buena implementación de esto porque sospecho que hay un montón de casos extremos que extrañaré si trato de escribirlo yo mismo?

(Por cierto, el motivo es hacer una instrumentación automática de funciones porque trabajamos mucho en dispositivos cerrados donde los perfiles de Javascript, etc. no están disponibles. Si hay una forma mejor que esto, entonces agradecería respuestas a lo largo de esas líneas también)

Respuesta

11

Aquí es una función wrap que llamar a los before y after funciones con exactamente los mismos argumentos y, si se suministra, el mismo valor para this:

var wrap = function (functionToWrap, before, after, thisObject) { 
    return function() { 
     var args = Array.prototype.slice.call(arguments), 
      result; 
     if (before) before.apply(thisObject || this, args); 
     result = functionToWrap.apply(thisObject || this, args); 
     if (after) after.apply(thisObject || this, args); 
     return result; 
    }; 
}; 

myFunc = wrap(myFunc, beforeExecute, afterExecute); 
+0

Voy a marcar esta como la respuesta correcta, porque creo que es la solución más completa al problema y maneja las funciones con un número arbitrario de argumentos. ¡Aclamaciones! Estaba esperando que haya una función de biblioteca estándar en jQuery para esto, pero siempre puedo agregarlo a nuestras extensiones. –

+0

He hecho una pequeña edición, de modo que el objeto 'regular' 'this' se usa si no se especificó 'thisObject' cuando se creó el contenedor. – Martijn

2

se podría hacer algo como:.

var wrap = function(func, pre, post) 
{ 
    return function() 
    { 
    var callee = arguments.callee; 
    var args = arguments; 

    pre();  
    func.apply(callee, args);  
    post(); 
    }; 
}; 

Esto permitiría que hagas:

var someFunc = function(arg1, arg2) 
{ 
    console.log(arg1); 
    console.log(arg2); 
}; 

someFunc = wrap(
    someFunc, 
    function() { console.log("pre"); }, 
    function() { console.log("post"); }); 

someFunc("Hello", 27); 

que da yo una salida en Firebug de:

pre 
Hello 
27 
post 

La parte importante cuando se envuelve esta manera, está pasando sus argumentos de la nueva función de nuevo a la función original.

+0

¿Por qué quieres para pasar el destinatario de la llamada como 'this'? – Martijn

2

Este es el ejemplo que me gustaría utilizar

<script type="text/javascript"> 
    var before = function(){alert("before")}; 
    var after = function(param){alert(param)}; 
    var wrap = function(func, wrap_before, wrap_after){ 
    wrap_before.call(); 
    func.call(); 
    wrap_after.call(); 
    }; 
    wrap(function(){alert("in the middle");},before,function(){after("after")}); 
</script> 
+0

Muestra también cómo parametrizar las funciones que está ejecutando con el ajuste. Si llamas a fuctions así con el parámetro whey, puedes ejecutarlo antes de envolverlo. – m4risU

+0

Esto no parece funcionar si la función que se envuelve tiene parámetros ...? –

+0

Es por eso que debe ajustar la función de parámetro en la llamada a function() {...}. De lo contrario, terminará disparando la función para obtener su resultado. – m4risU

-1

Tal vez me equivoque, pero creo que se puede crear directamente una función anónimo y asignarlo a myFunc:

 
myFunc = function(){ 
      BeforeFunction(); 
      myFunc(); 
      AfterFunction(); 
     } 

De esta manera, puedes controlar los argumentos de cada función.

4

La puesta en práctica aceptada no proporciona una opción para llamada envuelta (original) función condicionalmente.

Aquí es una mejor manera de envolver y desenvolver un método:

/* 
    Replaces sMethodName method of oContext with a function which calls the wrapper 
    with it's list of parameters prepended by a reference to wrapped (original) function. 

    This provides convenience of allowing conditional calls of the 
    original function within the wrapper, 

    unlike a common implementation that supplies "before" and "after" 
    cross cutting concerns as two separate methods. 

    wrap() stores a reference to original (unwrapped) function for 
    subsequent unwrap() calls. 

    Example: 
    ========================================= 

    var o = { 
     test: function(sText) { return sText; } 
    } 

    wrap('test', o, function(fOriginal, sText) { 
     return 'before ' + fOriginal(sText) + ' after'; 
    }); 
    o.test('mytext') // returns: "before mytext after" 

    unwrap('test', o); 
    o.test('mytext') // returns: "mytext" 

    ========================================= 
*/ 
function wrap(sMethodName, oContext, fWrapper, oWrapperContext) { 
    var fOriginal = oContext[sMethodName]; 

    oContext[sMethodName] = function() { 
     var a = Array.prototype.slice.call(arguments); 
     a.unshift(fOriginal.bind(oContext)); 
     return fWrapper.apply(oWrapperContext || oContext, a); 
    }; 
    oContext[sMethodName].unwrapped = fOriginal; 
}; 

/* 
    Reverts method sMethodName of oContext to reference original function, 
    the way it was before wrap() call 
*/ 
function unwrap(sMethodName, oContext) { 
    if (typeof oContext[sMethodName] == 'function') { 
     oContext[sMethodName] = oContext[sMethodName].unwrapped; 
    } 
}; 
Cuestiones relacionadas