Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

like image 823
Greg Beech Avatar asked Mar 10 '11 11:03

Greg Beech


People also ask

What is wrap function in jQuery?

jQuery wrap() method is used to wrap specified HTML elements around each selected element. The wrap () function can accept any string or object that could be passed through the $() factory function. Syntax: $(selector).

What is wrapping in JS?

In programming languages such as JavaScript, a wrapper is a function that is intended to call one or more other functions, sometimes purely for convenience, and sometimes adapting them to do a slightly different task in the process.

What is wrap method?

WRAP stands for Widen Your Options, Reality-Test Your Assumptions, Attain Distance Before Deciding, and Prepare to Be Wrong. Widen Your Frame. One of the main pitfalls in decision making is having a narrow frame. That means you don't consider possible alternatives that might be better options.


4 Answers

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);
like image 196
Martijn Avatar answered Nov 14 '22 09:11

Martijn


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;
    }
};
like image 32
Lex Avatar answered Nov 14 '22 09:11

Lex


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>
like image 27
m4risU Avatar answered Nov 14 '22 09:11

m4risU


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.

like image 2
Matthew Abbott Avatar answered Nov 14 '22 10:11

Matthew Abbott