开发者

Wrapping a function in Javascript / jQuery

开发者 https://www.devze.com 2023-02-15 19:45 出处:网络
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.

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开发者_JS百科 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.)


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


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


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>


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.


Maybe I'm wrong, but I think you can directly create an anonym function and assign it to myFunc:

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

In this way you can control the arguments of every function.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号