JavaScript Wrapping a function in Javascript / jQuery

If I have an arbitrary function myFunc, what I'm aiming to do is replace this function with a wrapped call that runs code before and after it executes, e.g.

// note: psuedo-javascript

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

myFunc = wrap(myFunc, beforeExecute, afterExecute);

However, I don't have an implementation of the required wrap function. Is there anything that already exists in jQuery like this (I've had a good look through the docs but cannot see anything)? Alternatively does anybody know of a good implementation of this because I suspect that there are a bunch of edge cases that I'll miss if I try to write it myself?

(BTW - the reason for this is to do some automatic instrumentation of functions because we do a lot of work on closed devices where Javascript profilers etc. are not available. If there's a better way than this then I'd appreciate answers along those lines too.)

Answer:1

Here’s a wrap function which will call the before and after functions with the exact same arguments and, if supplied, the same value for 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);
Answer:2

The accepted implementation does not provide an option to call wrapped (original) function conditionally.

Here is a better way to wrap and unwrap a method:

  /*
    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;
    }
};
Answer:3

You could do something like:

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

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

This would allow you to do:

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

Which gives me an output in Firebug of:

pre
Hello
27
post

The important part when wrapping this way, is passing your arguments from the new function back to the original function.

Answer:4

This is the example I would use

<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>
Answer:5

Question What's the full code to enable jQuery on a page and then use it to replace the contents of a DIV with HTML text from an external file? Background I'm brand new to jQuery and I'm eager to ...

Question What's the full code to enable jQuery on a page and then use it to replace the contents of a DIV with HTML text from an external file? Background I'm brand new to jQuery and I'm eager to ...

  1. jquery replace div contents with html

How do I load another page on the onClick event in Javascript? <div id="div_man" onclick="/subfolder/index.htm"></div> The code above is not working. Please explain. I am sure it's quite ...

How do I load another page on the onClick event in Javascript? <div id="div_man" onclick="/subfolder/index.htm"></div> The code above is not working. Please explain. I am sure it's quite ...

I've been trying to create a chart that looks similar to Google's Finance Charts. I've gotten almost everything I want with the Annotated Time Line Chart, except I can't seem to figure out how to ...

I've been trying to create a chart that looks similar to Google's Finance Charts. I've gotten almost everything I want with the Annotated Time Line Chart, except I can't seem to figure out how to ...

How can I utilize the new MobileSafari device motion APIs to capture a "shake" event?

How can I utilize the new MobileSafari device motion APIs to capture a "shake" event?